ConcurrentModificationException, вызванное подсписью

У меня очень простой код:

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

И я получаю исключение ConcurrentModificationException по list.add(0, d). Так что в общем, это из-за sublist(). Я очень смущен, потому что в случае подписок() в документации говорится:

Возвращенный список поддерживается этим списком, поэтому неструктурные изменения в возвращенном списке отражаются в этом списке и наоборот.

Не могли бы вы объяснить мне, где улов?

Ответ 1

subList - простой вид исходного списка (см. Здесь). Вам разрешено мутировать элементы внутри него, но не изменять структуру списка.

Согласно документации, поведение subList не определено, если вы пытаетесь внести структурные изменения. Я предполагаю, что в этой конкретной реализации ConcurrentModificationException было определено как неопределенное поведение.

Семантика списка, возвращаемого этим методом, становится неопределенной, если список поддержки (т.е. Этот список) структурно изменен каким-либо образом, кроме как через возвращенный список. (Структурные изменения - это те, которые изменяют размер этого списка или иным образом нарушают его таким образом, что выполняемые итерации могут давать неверные результаты.)

Ответ 2

Возвращенный список поддерживается этим списком, поэтому неструктурные изменения в возвращенном списке отражаются в этом списке и наоборот. Ссылка ссылки

Вышеизложенное утверждение абсолютно правильно, но мы должны иметь в виду неструктурные изменения. Я хотел бы изобразить два примера, оправдывающих вышеприведенное утверждение.
Пример-1: Выполнение неструктурного изменения в списке.

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

Output-:

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]

Explanation-: В соответствии с вышеупомянутым выражением о документации 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);
    }

Output-:

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]

Explanation-: В соответствии с вышеупомянутым выражением о документации 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);
    }

Output-:

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)

Explanation-: В соответствии с вышеупомянутым выражением 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);
    }

Output-:

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]

Explanation-: Структурная модификация возвращенного списка работает хорошо и полностью отражается в списке.

Ответ 3

list.add(0, d) включает перемещение всех элементов по одной позиции и увеличение размера списка. Это довольно структурные изменения.

Ответ 4

Сценарий возникновения этой ошибки

  • У меня был список (исходный список), и пусть 100 предметов
  • Сортировка исходного списка в порядке возрастания
  • Подсчитайте это → созданный подсписщик (восходящий упорядоченный подсписчик)
  • Сортировка исходного списка в порядке убывания
  • Перечислить список подписок (по возрастанию упорядоченного списка)

Получено исключение параллельной модификации

Исправление к вышеуказанному сценарию

  • У меня был список (исходный список), и пусть 100 предметов
  • Сортировка по возрастанию
  • Подсчитайте это → созданный подсписщик (восходящий упорядоченный подсписчик)
  • Перечислить список подписок (восходящий упорядоченный подсписок)
  • Сортировка исходного списка в порядке убывания