为什么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
操作:
返回的迭代器提供构造迭代器时列表状态的快照。 遍历迭代器时不需要同步。 迭代器不支持
remove
,set
或add
方法 。
这解释了使用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 super E> 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(); } }