2

I have the below class for checking removing of object from arraylist by using foreach loop

import java.util.ArrayList;

    public class CheckArrayList {
        /**
         * @return the a
         */
        public int getA() {
            return a;
        }

        /**
         * @param a
         *            the a to set
         */
        public void setA(int a) {
            this.a = a;
        }

        /**
         * @return the b
         */
        public int getB() {
            return b;
        }

        /**
         * @param b
         *            the b to set
         */
        public void setB(int b) {
            this.b = b;
        }

        int a;
        int b;

        public static void main(String[] args) {
            try {
                CheckArrayList checkArrayList1 = new CheckArrayList();
                checkArrayList1.setA(10);
                checkArrayList1.setB(20);

                CheckArrayList checkArrayList2 = new CheckArrayList();
                checkArrayList2.setA(30);
                checkArrayList2.setB(40);

                ArrayList<CheckArrayList> aList = new ArrayList<CheckArrayList>();

                aList.add(checkArrayList1);
                aList.add(checkArrayList2);

                System.out.println("size of list before iterate = " + aList.size());
                for (CheckArrayList checkArrayList : aList) {
                    aList.remove(checkArrayList);
                    System.out.println("inside loop");
                }
                System.out.println("size of list after iterate = " + aList.size());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

while doing above operation it is not showing any exception but it comes inside the loop only once.

if I add one more object to ist and doing same operation it showing java.util.ConcurrentModificationException

import java.util.ArrayList;

public class CheckArrayList {
    /**
     * @return the a
     */
    public int getA() {
        return a;
    }

    /**
     * @param a
     *            the a to set
     */
    public void setA(int a) {
        this.a = a;
    }

    /**
     * @return the b
     */
    public int getB() {
        return b;
    }

    /**
     * @param b
     *            the b to set
     */
    public void setB(int b) {
        this.b = b;
    }

    int a;
    int b;

    public static void main(String[] args) {
        try {
            CheckArrayList checkArrayList1 = new CheckArrayList();
            checkArrayList1.setA(10);
            checkArrayList1.setB(20);

            CheckArrayList checkArrayList2 = new CheckArrayList();
            checkArrayList2.setA(30);
            checkArrayList2.setB(40);

            CheckArrayList checkArrayList3 = new CheckArrayList();
            checkArrayList3.setA(30);
            checkArrayList3.setB(40);

            ArrayList<CheckArrayList> aList = new ArrayList<CheckArrayList>();

            aList.add(checkArrayList1);
            aList.add(checkArrayList2);
            aList.add(checkArrayList3);

            System.out.println("size of list before iterate = " + aList.size());
            for (CheckArrayList checkArrayList : aList) {
                aList.remove(checkArrayList);
                System.out.println("inside loop");
            }
            System.out.println("size of list after iterate = " + aList.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

why it not showing exception if arralist size equal to 2?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Niju
  • 487
  • 1
  • 9
  • 18

2 Answers2

3

To answer why it doesn't throw an error on 2 elements you have to look at what the for each loop actually does.for(Type something:myList) compiles to something like:

Iterator<Type> iter = myList.iterator();
while(iter.hasNext()){
     Type something= iter.next()
     //your code
}

Now look at the source code for ArrayList for the actual implementation. The code for hasNext() is:

return cursor != size; //cursor is the current position in the list

So when you remove the element in the loop, the size is now 1. When it looks to see if there are more elements it will return false because it thinks that is at the end of the ArrayList, and stops executing the loop at this point.

Now look at the code for next():

public E next() {
        checkForComodification();
        //more code
}

This is where the actual checking for modification occurs, so when size is 3, it will execute the second iteration and when it calls next() to get the second element, it will thrown an exception because you have modified the ArrayList outside of the iterator.

vandale
  • 3,600
  • 3
  • 22
  • 39
2

You must use Iterator interface to accomplish this task.

Iterator iterator = aList.iterator();
while(iterator.hasNext) {
     CheckArrayList obj = iterator.next();
     // use some condition to check which object to remove
     if(condition) {
          iterator.remove();
     }
}

Edit: Answer for your edit. You are iterating on the list from 0 to SIZE and in the loop you are removing items (which already against the best practices). This removal reduces the size of the list which will fail when you try to access the indexes which are greater than the effective size (the size after the deleted items).

From Java docs

The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

Hope this helps.

jagmohan
  • 2,052
  • 2
  • 26
  • 41
  • 1
    That's ok but why it is not showing exception when the arraylist size is 2? – Niju Nov 18 '13 at 04:26
  • 1
    Or you could use a `CopyOnWriteArrayList` – Josh M Nov 18 '13 at 04:37
  • 1
    @pikrut Code probably not throwing an exception with size 2 because loop is exited immediately after first removal. Post-removal, list length is 1 and iterator's `hasNext()` implementation returns false. I suppose one could argue that this is a bug? I haven't really looked into it and am just speculating. As others have pointed out, you should either use an iterator or run 'foreach' loop against a clone of original list. – Hollis Waite Nov 18 '13 at 04:50
  • 1
    Behavior is deterministic. I'd just say that it's confusing. Looking at Java source, you can see that `checkForComodification()` is called in `ArrayList.Itr.remove()` and `ArrayList.Itr.next()`. However, there is no such call in `hasNext()`. That's why the method happily returns false instead of blowing up. Once you list reaches size 3, execution flow reaches a method that will fail fast. – Hollis Waite Nov 18 '13 at 04:59