ITmob-Ly
发布于 2022-12-24 / 385 阅读
0

Kotlin Exception - NoSuchElementException: Collection contains no element matching the predicate

1. 异常

Process: cn.itmob.demo, PID: 26458
java.util.NoSuchElementException: Collection contains no element matching the predicate.
	at cn.itmob.demo.GetPostDetails.execute(GetPostDetails.kt:45)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
	Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@a5402d1, Dispatchers.Default]

或者

FATAL EXCEPTION: main
Process: cn.itmob.demo, PID: 3224
java.util.NoSuchElementException: Expected at least one element
	at kotlinx.coroutines.flow.FlowKt__ReduceKt.first(Reduce.kt:96)
	at kotlinx.coroutines.flow.FlowKt.first(Unknown Source:1)
	at cn.itmob.demo.MainActivity$onCreate$1.invokeSuspend(MainActivity.kt:47)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:367)
	at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
	at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25)
	at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110)
	at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
	at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56)
	at kotlinx.coroutines.BuildersKt.launch(Unknown Source:1)
	at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47)
	at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source:1)
	at cn.itmob.demo.MainActivity.onCreate(MainActivity.kt:46)
	at android.app.Activity.performCreate(Activity.java:7994)
	at android.app.Activity.performCreate(Activity.java:7978)
	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
	at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
	at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
	at android.os.Handler.dispatchMessage(Handler.java:106)
	at android.os.Looper.loop(Looper.java:223)
	at android.app.ActivityThread.main(ActivityThread.java:7656)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
	Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@2a9a367, Dispatchers.Main.immediate]

解决

NoSuchElementException 在刚刚使用 Kotlin 或不小心时就会遇到,主要集中在使用 集合、String、 或 Flow 时使用 firstlastminmaxsinglerandom 等时。

如下是源码文件 _Collections.kt _Strings.kt Reduce.kt 中关于 NoSuchElementException 异常的部分源码:

// _Collections.kt
/**
 * Returns the first element.
 * 
 * @throws NoSuchElementException if the list is empty.
 */
public fun <T> List<T>.first(): T {
    if (isEmpty())
        throw NoSuchElementException("List is empty.")
    return this[0]
}

public fun <T> List<T>.last(): T {
    if (isEmpty())
        throw NoSuchElementException("List is empty.")
    return this[lastIndex]
}

public fun <T> Collection<T>.random(random: Random): T {
    if (isEmpty())
        throw NoSuchElementException("Collection is empty.")
    return elementAt(random.nextInt(size))
}
// Reduce.kt
/**
 * The terminal operator that returns the first element emitted by the flow and then cancels flow's collection.
 * Throws [NoSuchElementException] if the flow was empty.
 */
public suspend fun <T> Flow<T>.first(): T {
    var result: Any? = NULL
    collectWhile {
        result = it
        false
    }
    if (result === NULL) throw NoSuchElementException("Expected at least one element")
    return result as T
}
// _Strings.kt
public fun CharSequence.first(): Char {
    if (isEmpty())
        throw NoSuchElementException("Char sequence is empty.")
    return this[0]
}

NoSuchElementException 的字母意思和源码所示这个异常出现在我们要获根据某些条件取值但是集合或FLow为空(empty)或没有符合条件的值时出现。

解决方法主要是:

  1. 涉及集合、Flow取值时考虑到没值可取的情况,确保不会出现 NoSuchElement 的情况。
  2. 使用 firstOrNull lastOrNull maxOrNull 等方法,Flow Collections String 中都有类似方法。