【问题标题】:ConcurrentModificationException thrown by sublist [duplicate]子列表引发的 ConcurrentModificationException [重复]
【发布时间】:2012-01-11 10:18:17
【问题描述】:

我有非常简单的代码:

    List<String> list = new ArrayList<String>();
    String a = "a";
    String b = "b";
    String c = "c";
    String d = "d";

    list.add(a);
    list.add(b);
    list.add(c);

    List<String> backedList = list.subList(0, 2);
    list.add(0, d); 
    System.out.println("2b: " + backedList);

我通过 list.add(0, d) 得到 ConcurrentModificationException 异常。所以总的来说,这是因为 sublist()。我很困惑,因为在 sublist() 的情况下,文档说:

返回的列表是由这个列表支持的,所以非结构性的变化 返回的列表反映在此列表中,反之亦然

你能解释一下问题在哪里吗?

【问题讨论】:

  • “捕获”是向列表添加元素是一种结构变化。
  • 当您提到@peter 时,该句子中的反之亦然具有误导性,因为作为文档(和您的代码),稍后解释,修改支持列表而不通过子列表将引发 ConcurrentModificationException

标签: java list


【解决方案1】:

subList 是原始列表的简单视图(请参阅here)。您可以更改其中的元素,但不能更改列表的结构。

根据文档,如果您尝试进行结构更改,subList 的行为是未定义的。我猜在这个特定的实现中,ConcurrentModificationException 被确定为未定义的行为。

如果后备列表(即此列表)以除通过返回列表之外的任何方式进行结构修改,则此方法返回的列表的语义将变为未定义。 (结构性修改是改变这个列表的大小,或者以其他方式扰乱它,使得正在进行的迭代可能产生不正确的结果。)

【讨论】:

  • 我浪费了一天的时间来检查到底出了什么问题,谢谢这帮助我重新排列了代码中的行
  • 请参阅我在下面添加的答案以进一步澄清上述评论
【解决方案2】:

返回列表由该列表支持,因此返回列表中的非结构性更改会反映在该列表中,反之亦然。 Reference Link

以上陈述绝对正确,但我们必须牢记非结构性变化。我想描述两个示例来证明上述陈述的合理性。
示例 1:list 中执行非结构更改。

public static void main(String[] args) {
        List<String> listArr = new ArrayList<>();
        listArr.add("Delhi");
        listArr.add("Bangalore");
        listArr.add("New York");
        listArr.add("London");

        List<String> listArrSub = listArr.subList(1, 3);

        System.out.println("List-: " + listArr);
        System.out.println("Sub List-: " + listArrSub);

        //Performing Non-Structural Change in list.
        Collections.swap(listArr, 0, 1);

        System.out.println("\nAfter Non-Structural Change...\n");

        System.out.println("List-: " + listArr);
        System.out.println("Sub List-: " + listArrSub);
    }

输出-:

List-: [Delhi, Bangalore, New York, London]
Sub List-: [Bangalore, New York]

After Non-Structural Change...

List-: [Bangalore, Delhi, New York, London]
Sub List-: [Delhi, New York]

说明-:根据上述Oracle的文档声明,交换操作反映在两个列表中。

示例 2:子列表中执行非结构性更改。

public static void main(String[] args) {
        List<String> listArr = new ArrayList<>();
        listArr.add("Delhi");
        listArr.add("Bangalore");
        listArr.add("New York");
        listArr.add("London");

        List<String> listArrSub = listArr.subList(1, 3);

        System.out.println("List-: " + listArr);
        System.out.println("Sub List-: " + listArrSub);

        //Performing Non-Structural Change in sub list.
        Collections.swap(listArrSub, 0, 1);

        System.out.println("\nAfter Non-Structural Change...\n");

        System.out.println("List-: " + listArr);
        System.out.println("Sub List-: " + listArrSub);
    }

输出-:

List-: [Delhi, Bangalore, New York, London]
Sub List-: [Bangalore, New York]

After Non-Structural Change...

List-: [Delhi, New York, Bangalore, London]
Sub List-: [New York, Bangalore]

说明-:根据上述Oracle的文档声明,交换操作反映在两个列表中,但它已在子列表中执行。

正如我们在上述两个示例中看到的非结构性变化。现在让我们根据 Oracle 文档中给出的以下语句查看 结构 更改。

如果后备列表(即此列表)以除通过返回列表之外的任何方式进行结构修改,则此方法返回的列表的语义将变为未定义。 (结构性修改是改变这个列表的大小,或者以其他方式扰乱它,使得正在进行的迭代可能产生不正确的结果。)

示例 3:在列表中执行结构更改。

 public static void main(String[] args) {
        List<String> listArr = new ArrayList<>();
        listArr.add("Delhi");
        listArr.add("Bangalore");
        listArr.add("New York");
        listArr.add("London");

        List<String> listArrSub = listArr.subList(1, 3);

        System.out.println("List-: " + listArr);
        System.out.println("Sub List-: " + listArrSub);

        //Performing Structural Change in list.
        listArr.add("Mumbai");

        System.out.println("\nAfter Structural Change...\n");

        System.out.println("List-: " + listArr);
        System.out.println("Sub List-: " + listArrSub);
    }

输出-:

List-: [Delhi, Bangalore, New York, London]
Sub List-: [Bangalore, New York]

After Structural Change...

List-: [Delhi, Bangalore, New York, London, Mumbai]
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1231)
    at java.util.ArrayList$SubList.listIterator(ArrayList.java:1091)
    at java.util.AbstractList.listIterator(AbstractList.java:299)
    at java.util.ArrayList$SubList.iterator(ArrayList.java:1087)
    at java.util.AbstractCollection.toString(AbstractCollection.java:454)
    at java.lang.String.valueOf(String.java:2982)
    at java.lang.StringBuilder.append(StringBuilder.java:131)
    at infosys.Research.main(Research.java:26)

说明-:根据上述Oracle的文档声明,结构修改操作会抛出java.util.ConcurrentModificationException异常除了通过返回列表之外的任何方式。

示例 4:子列表中执行结构更改。

public static void main(String[] args) {
        List<String> listArr = new ArrayList<>();
        listArr.add("Delhi");
        listArr.add("Bangalore");
        listArr.add("New York");
        listArr.add("London");

        List<String> listArrSub = listArr.subList(1, 3);

        System.out.println("List-: " + listArr);
        System.out.println("Sub List-: " + listArrSub);

        //Performing Structural Change in sub list.
        listArrSub.add("Mumbai");

        System.out.println("\nAfter Structural Change...\n");

        System.out.println("List-: " + listArr);
        System.out.println("Sub List-: " + listArrSub);
    }

输出-:

List-: [Delhi, Bangalore, New York, London]
Sub List-: [Bangalore, New York]

After Structural Change...

List-: [Delhi, Bangalore, New York, Mumbai, London]
Sub List-: [Bangalore, New York, Mumbai]

说明-:结构修改返回列表运行良好,并完全反映在列表中。

【讨论】:

    【解决方案3】:

    list.add(0, d) 涉及将所有项目移动一个位置并增加列表的大小。这是相当结构性的变化。

    【讨论】:

      【解决方案4】:

      遇到此错误的场景

      • 我有一个列表(原始列表),比如说 100 项
      • 按升序对原始列表进行排序
      • 子列表 -> 创建子列表(升序子列表)
      • 按降序对原始列表进行排序
      • 迭代子列表(升序子列表)列表

      并发修改异常

      修复上述情况

      • 我有一个列表(原始列表),比如说 100 项
      • 升序排列
      • 子列表 -> 创建子列表(升序子列表)
      • 迭代子列表(升序子列表)列表
      • 按降序对原始列表进行排序

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-06-24
        • 2020-01-29
        • 1970-01-01
        • 2017-02-09
        • 1970-01-01
        • 1970-01-01
        • 2011-03-12
        • 2021-07-01
        相关资源
        最近更新 更多