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

邮差的信 提交于 2019-12-30 04:06:33

问题


I can sort a list of users without any problems using the following code and Java 8:

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

Now, I changed to Java 7 and I saw no errors on eclipse. But now, when running under Java 7 I got this error:

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?


回答1:


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 Collections.sort(list, c) specifies that:

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.

Looking at the code, this is done by obtaining a ListIterator from the list. However, CopyOnWriteArrayList listIterator() method states that the iterator returned does not support the set operation:

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.

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.


In Java 8, Collections.sort(list, c) changed implementation:

This implementation defers to the List.sort(Comparator) method using the specified list and comparator.

And the new method CopyOnWriteArrayList.sort(c) (introduced in Java 8) does not use the list iterator so it works correctly.




回答2:


Collections.sort(), in Java 7 (and early versions of Java 8) uses the list iterator's set() to modify the list. But CopyOnWriteArrayList's iterator is explicitly documented as not supporting this operation.

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.

I strongly suggest to make your comparator much simpler by using Integer.compare() and Boolean.compare().




回答3:


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. The default implementation of that sort method seems to be the previous behaviour (i.e. using List.listIterator(), which would still fail with an UnsupportedOperationException), but the CopyOnWriteArrayList explicitly provides a different version which does not use an 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();
    }
}


来源:https://stackoverflow.com/questions/34827309/why-does-collections-sortlist-work-in-java-8-with-copyonwritearraylist-but-not

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!