24

I have very simple code:

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

And I get ConcurrentModificationException exception by list.add(0, d). So in general, it's because of sublist(). I'm very confused, because in case of sublist() the documentation says:

The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa.

Could you explain me where the catch is?

peter
  • 631
  • 2
  • 7
  • 18
  • 3
    The "catch" is that adding an element to a list is a **structural** change. – Stephen C Jan 11 '12 at 10:26
  • the vice-versa in that sentence is misleading as you mention @peter, because as the docs (and your code), explain later, modifying the backed list without going through the sublist will raise a ConcurrentModificationException – David Santiago Turiño Apr 20 '15 at 15:23

4 Answers4

21

The subList is simple a view of the original list (see here). You are allowed to mutate elements within it but not change the structure of the list.

According to the documentation, subList behaviour is undefined if you try to make structural changes. I guess in this particular implementation, ConcurrentModificationException was decided as the undefined behaviour.

The semantics of the list returned by this method become undefined if the backing list (i.e., this list) is structurally modified in any way other than via the returned list. (Structural modifications are those that change the size of this list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.)

Jeff Foster
  • 43,770
  • 11
  • 86
  • 103
7

The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa. Reference Link

Above statement is absolutely correct but we have to keep in mind non-structural change. I would like to depict two examples justifying above statement.
Example-1: Performing Non-Structural change in 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);
    }

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-: As per aforementioned Oracle's documentation statement, swapping operation is reflected in both the lists.

Example-2: Performing Non-Structural change in sub 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 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-:As per aforementioned Oracle's documentation statement, swapping operation is reflected in both the lists however it has been performed on sub list.

As we have seen Non-Structural changes in the aforementioned two examples. Now let's see Structural changes as per below statement which is given in the Oracle's documentation.

The semantics of the list returned by this method become undefined if the backing list (i.e., this list) is structurally modified in any way other than via the returned list. (Structural modifications are those that change the size of this list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.)

Example-3: Performing Structural change in 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 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-:As per aforementioned Oracle's documentation statement, structural modification operation is throwing java.util.ConcurrentModificationException exception whenever The semantics of the list returned by this method become undefined if the backing list (i.e., this list) is structurally modified in any way other than via the returned list.

Example-4: Performing Structural change in sub 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 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-: Structural modification to returned list is working well and reflecting in the list altogether.

Ashish Kumar
  • 916
  • 2
  • 15
  • 32
0

Scenario in which this error was encountered

  • I had a list(original list) with let's say 100 item
  • Sort original list in ascending order
  • Sublist it -> created sublist (ascending ordered sublist)
  • Sort original list in descending order
  • Iterate over sublist(ascending ordered sublist) list

Got concurrent modification exception

Fix to above scenario

  • I had a list(original list) with let's say 100 item
  • Sort it in ascending order
  • Sublist it -> created sublist (ascending ordered sublist)
  • Iterate over sublisted(ascending ordered sublist) list
  • Sort original list in descending order
Akshay Joshi
  • 467
  • 1
  • 9
  • 22
0

list.add(0, d) involves moving all items by one position and increasing list's size. It's quite structural change.

Vadzim
  • 24,954
  • 11
  • 143
  • 151