问题
I'm sorting a List using Collections.sort(), which seems to be working every way I can test it. Some of my users are crashing though.
Caused by java.lang.UnsupportedOperationException
java.util.AbstractList.set (AbstractList.java:681)
java.util.AbstractList$FullListIterator.set (AbstractList.java:143)
java.util.Collections.sort (Collections.java:1909)
com.myapp.MyFragment.myMethod (MyFragment.java:225)
But, all I'm doing is trying to sort a List
private void myMethod(List<MyThing> myThings) {
Collections.sort(myList, (thing1, thing2) -> Long.compare(thing2.getLongValue(), thing1.getLongValue()));
I'm noticing this doesnt happen on Android 8 and above. Its only happening on 5.0 - 7.1
The list is instantiated as an ArrayList, populated, and set as a member of an instance of a class as a generic list. This object is then posted with EventBus. The list is then mapped and sorted using Kotlin's map
and sortedByDescending
functions. The mapped and sorted list is then passed to myMethod()
This Kotlin extension is generating the list passed to this method:
fun List<MyThing>.mapAndSort(context: Context): List<MyThing> {
return mapNotNull {
when (it.type) {
type -> it.getTyped(context) //all of these return polymorphic MyThings
anotherType -> it.getAnotherTyped(context)
someOtherType -> it.getSomeOtherTyped(context)
yetAnotherType -> it.getYetAnotherType(context)
yetOneMoreType -> it.getYetOneMoreType(context)
finallyLastTyope -> it.getFinallyLastType(context)
else -> null
}
}.sortedByDescending { it.longValue }
}
So far I've seen this list be a java.util.Arrays$ArrayList
, and kotlin.collections.EmptyList
.
I'm thinking this has something to do with EventBus, but may be Kotlin passing a Kotlin collections list type. Does anyone know exactly what's going on?
回答1:
The source of this crash seems to be that, internally, Iterable<T>.sortedWith
(which is used by Iterable<T>.sortedByDescending
) creates a new list if the size of the Iterable
is 0 or 1:
public fun <T> Iterable<T>.sortedWith(comparator: Comparator<in T>): List<T> {
if (this is Collection) {
if (size <= 1) return this.toList() // <-- HERE
@Suppress("UNCHECKED_CAST")
return (toTypedArray<Any?>() as Array<T>).apply { sortWith(comparator) }.asList()
}
return toMutableList().apply { sortWith(comparator) }
}
The function toList()
in turn will call listOf
:
public fun <T> Iterable<T>.toList(): List<T> {
if (this is Collection) {
return when (size) {
0 -> emptyList()
1 -> listOf(if (this is List) get(0) else iterator().next())
else -> this.toMutableList()
}
}
return this.toMutableList().optimizeReadOnlyList()
}
Which in turn will resolve into either an EmptyList
(which is safe to sort since it's empty) or a SingletonList
which doesn't implement set
from AbstractList
Here is some evidence:
https://try.kotlinlang.org/#/UserProjects/s3j6g8476h8047vvvsjuvletlr/rjlkkn3n6117c1aagja8dr1h67
We could potentially have a bug or, at least, an improvement since sortedWith
could return this
if it's of type List
:
public fun <T> Iterable<T>.sortedWith(comparator: Comparator<in T>): List<T> {
if (this is Collection) {
if (size <= 1) return if(this is List<T>) this else this.toList()
@Suppress("UNCHECKED_CAST")
return (toTypedArray<Any?>() as Array<T>).apply { sortWith(comparator) }.asList()
}
return toMutableList().apply { sortWith(comparator) }
}
回答2:
Collections.sort
is an in-place list mutating operation. If you're going to invoke it in your Java method, do only pass a list there that is statically known to be a MutableList
in Kotlin.
If you have a List
of unknown mutability, you can get a MutableList
from it by calling .toMutableList()
extension function on it.
And if it's not principal to you to have myMethod
written in Java, you can replace it with list.sortedByDescending { it.longValue }
call in Kotlin, then you wouldn't need to turn that list into a mutable list first. In fact that's what mapAndSort
method does in the end, so it looks like there's no need to sort it again.
来源:https://stackoverflow.com/questions/55802573/abstractlist-unsupportedoperationexception-when-calling-sort-on-a-list