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

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

我可以使用以下代码和 Java 8:

毫无问题地对用户列表进行排序
CopyOnWriteArrayList<User> 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)

如何解决?

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

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

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

Java 7(和 Java 8 的早期版本)和 Java 8u20 在 Collections.sort 工作方式(issue 8032636, as noted by Holger ).


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.

Looking at the code, this is done by obtaining a ListIterator from the list. However, 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.

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


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

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

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

在 Java 8 中,Collections.sort(List, Comparator) 的实现已更改为现在重定向到 new sort method in the List interfacesort 方法的默认实现似乎是以前的行为(即使用 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();
    }
}