为什么Collections.sort(List)在Java 8中使用CopyOnWriteArrayList但在Java 7中不起作用?

我可以使用以下代码和Java 8对用户列表进行排序而没有任何问题:

CopyOnWriteArrayList allCurrentLoginnedUsersList = new CopyOnWriteArrayList(); Collections.sort(allCurrentLoginnedUsersList); 

现在,我改为Java 7,我在eclipse上看到没有错误。 但是现在,在Java 7下运行时出现了这个错误:

 java.lang.UnsupportedOperationException at java.util.concurrent.CopyOnWriteArrayList$COWIterator.set(CopyOnWriteArrayList.java:1049) at java.util.Collections.sort(Collections.java:221) at com.fluent.User.sortAllCurrentLoginnedUsers(User.java:446) 

怎么解决?

Java 7(和早期版本的Java 8)和Java 8u20在Collections.sort工作方式上有所变化 ( 问题8032636 ,如Holger所述 )。


Java 7 Collections.sort(list, c)指定:

此实现将指定的列表转储到数组中,对数组进行排序,并迭代列表,从数组中的相应位置重置每个元素。 这样可以避免尝试对链接列表进行排序所导致的n²log(n)性能。

查看代码 ,这是通过从列表中获取ListIterator来完成的。 但是, CopyOnWriteArrayList listIterator()方法声明返回的迭代器不支持set操作:

返回的迭代器提供构造迭代器时列表状态的快照。 遍历迭代器时不需要同步。 迭代器不支持removesetadd方法

这解释了使用Java 7运行代码时出现的错误。作为一种解决方法,您可以参考这个问题 ,其中的答案是将列表的内容转储到数组中,对数组进行排序并将元素放回到名单。


在Java 8中, Collections.sort(list, c)改变了实现:

此实现List.sort(Comparator)使用指定列表和比较器的List.sort(Comparator)方法。

并且新方法CopyOnWriteArrayList.sort(c) (在Java 8中引入)不使用列表迭代器,因此它可以正常工作。

Java 7中的Collections.sort() (以及Java 8的早期版本)使用列表迭代器的set()来修改列表。 但是CopyOnWriteArrayList的迭代器被明确记录为不支持此操作。

怎么解决? 将CopyOnWriteArrayList转换为常规ArrayList或数组,对其进行排序,然后清除CopyOnWriteArrayList并使用排序列表再次填充它。

我强烈建议使用Integer.compare()Boolean.compare()使比较器更简单。

在Java 8中, Collections.sort(List, Comparator)的实现已更改为现在重定向到List接口中的新sort方法 。 该sort方法的默认实现似乎是先前的行为(即使用List.listIterator() ,它仍然会因UnsupportedOperationException而失败),但CopyOnWriteArrayList显式提供了不使用Iterator的不同版本:

 public void sort(Comparator c) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); Object[] newElements = Arrays.copyOf(elements, elements.length); @SuppressWarnings("unchecked") E[] es = (E[])newElements; Arrays.sort(es, c); setArray(newElements); } finally { lock.unlock(); } }