【问题标题】:Why does Collections.sort(List) work in Java 8 with CopyOnWriteArrayList but not in Java 7?为什么 Collections.sort(List) 在 Java 8 中与 CopyOnWriteArrayList 一起工作,但在 Java 7 中却不行?
【发布时间】:2016-04-22 00:08:04
【问题描述】:

我可以使用以下代码和 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)

如何解决?

【问题讨论】:

    标签: java collections java-8 java-7


    【解决方案1】:

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


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

    这个实现将指定的列表转储到一个数组中,对数组进行排序,然后遍历列表,从数组中的相应位置重置每个元素。这避免了尝试对链表进行就地排序而导致的 n² log(n) 性能。

    Looking at the code,这是通过从列表中获取ListIterator 来完成的。但是CopyOnWriteArrayListlistIterator()方法声明返回的迭代器不支持set操作:

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

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


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

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

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

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • 不是运气,Collections.sort 在 Java 8 中更改了实现 :)
      • 奇怪。我在我的 Java 8 源代码版本中没有看到这种变化。也许它太旧了。在我的版本 (1.8.0-b132) 中,List.sort() 调用 Collections.sort()。
      • 啊,这确实很奇怪。 Grepcode 显示了变化。
      【解决方案3】:

      在 Java 8 中,Collections.sort(List, Comparator) 的实现已更改为现在重定向到 new sort method in the List interfacesort 方法的默认实现似乎是以前的行为(即使用 List.listIterator(),它仍然会因 UnsupportedOperationException 而失败),但 CopyOnWriteArrayList 明确提供了不使用 @987654329 的不同版本@:

      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();
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-12-03
        • 2019-11-27
        • 1970-01-01
        • 1970-01-01
        • 2018-08-10
        • 1970-01-01
        • 2014-08-19
        • 1970-01-01
        相关资源
        最近更新 更多