简体   繁体   English

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

[英]Why does Collections.sort(List) work in Java 8 with CopyOnWriteArrayList but not in Java 7?

I can sort a list of users without any problems using the following code and Java 8: 我可以使用以下代码和Java 8对用户列表进行排序而没有任何问题:

CopyOnWriteArrayList<User> allCurrentLoginnedUsersList = new CopyOnWriteArrayList<>(); 
Collections.sort(allCurrentLoginnedUsersList);

Now, I changed to Java 7 and I saw no errors on eclipse. 现在,我改为Java 7,我在eclipse上看到没有错误。 But now, when running under Java 7 I got this error: 但是现在,在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)

How to fix it? 怎么解决?

There was a change between Java 7 (and early version of Java 8) and Java 8u20 in the way Collections.sort work ( issue 8032636 , as noted by Holger ). Java 7(和早期版本的Java 8)和Java 8u20之间的Collections.sortCollections.sort的方式发生了变化( 问题8032636 ,如Holger所述 )。


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

This implementation dumps the specified list into an array, sorts the array, and iterates over the list resetting each element from the corresponding position in the array. 此实现将指定的列表转储到数组中,对数组进行排序,并迭代列表,从数组中的相应位置重置每个元素。 This avoids the n² log(n) performance that would result from attempting to sort a linked list in place. 这样可以避免尝试对链接列表进行排序所导致的n²log(n)性能。

Looking at the code , this is done by obtaining a ListIterator from the list. 查看代码 ,这是通过从列表中获取ListIterator来完成的。 However, CopyOnWriteArrayList listIterator() method states that the iterator returned does not support the set operation: 但是, CopyOnWriteArrayList listIterator()方法声明返回的迭代器不支持set操作:

The returned iterator provides a snapshot of the state of the list when the iterator was constructed. 返回的迭代器提供构造迭代器时列表状态的快照。 No synchronization is needed while traversing the iterator. 遍历迭代器时不需要同步。 The iterator does NOT support the remove , set or add methods . 迭代器不支持removesetadd方法

This explains the error you are getting when running your code with Java 7. As a workaround, you can refer to this question where the answer is to dump the content of the list into an array, sort the array and put the elements back in the list. 这解释了使用Java 7运行代码时出现的错误。作为一种解决方法,您可以参考这个问题 ,其中的答案是将列表的内容转储到数组中,对数组进行排序并将元素放回到名单。


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

This implementation defers to the List.sort(Comparator) method using the specified list and comparator. 此实现List.sort(Comparator)使用指定列表和比较器的List.sort(Comparator)方法。

And the new method CopyOnWriteArrayList.sort(c) (introduced in Java 8) does not use the list iterator so it works correctly. 新方法CopyOnWriteArrayList.sort(c) (在Java 8中引入)不使用列表迭代器,因此它可以正常工作。

Collections.sort() , in Java 7 (and early versions of Java 8) uses the list iterator's set() to modify the list. Java 7中的Collections.sort() (以及Java 8的早期版本)使用列表迭代器的set()来修改列表。 But CopyOnWriteArrayList's iterator is explicitly documented as not supporting this operation. 但是CopyOnWriteArrayList的迭代器被明确记录为不支持此操作。

How to fix it? 怎么解决? Transform your CopyOnWriteArrayList into a regular ArrayList or array, sort that, and then clear the CopyOnWriteArrayList and fill it again with the sorted list. CopyOnWriteArrayList转换为常规ArrayList或数组,对其进行排序,然后清除CopyOnWriteArrayList并使用排序列表再次填充它。

I strongly suggest to make your comparator much simpler by using Integer.compare() and Boolean.compare() . 我强烈建议使用Integer.compare()Boolean.compare()使比较器更简单。

In Java 8 the implementation of Collections.sort(List, Comparator) has been changed to now redirect to a new sort method in the List interface . 在Java 8中, Collections.sort(List, Comparator)的实现已更改为现在重定向到List接口中sort方法 The default implementation of that sort method seems to be the previous behaviour (ie using List.listIterator() , which would still fail with an UnsupportedOperationException ), but the CopyOnWriteArrayList explicitly provides a different version which does not use an Iterator : 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();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM