-1

I have to questions. The first one is why when we run this function we have ConcurrentModificationException ?

public static void testList() {
    List<String> list = new ArrayList<String>();
    list.add("str3");

    for (String st : list) {
        if (st.equalsIgnoreCase("str3")) {
            list.remove("str3");
        }
    }
    System.out.println(list);
}

I thing, because enhanced for use Iterator (which checks modificationsCount), but I ask to be sure. Is that the reason for the exception.

The second question is if I useCollections.synchronizedList(new LinkedList<Something>()); can I use 2 or more enhanced for loops? For example I have to threads and at some time the first one remove element from the collection and at some time the second one add elements in collection. I thing that it should be thread save even when we use iterator (I thing that enhanced for use iterator).
Thanks in advance.

Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
DPM
  • 1,540
  • 2
  • 18
  • 40

3 Answers3

3

You will get a ConcurrentModificationException if you remove elements from a list while you are iterating over the list, as you are doing in your example above. To avoid this, you should use an explicit iterator instead of the enhanced for loop, and call remove() on the iterator instead of on the list when you find the element that must be removed:

for (Iterator<String> i = list.iterator(); i.hasNext(); ) {
    String st = i.next();
    if (st.equalsIgnoreCase("str3")) {
        // Remove the element that the iterator is currently pointing to
        i.remove();
    }
}

Just wrapping the list with Collections.synchronizedList(...) does not make it so that you can have multiple threads iterating over the list at the same time, where one of them is removing elements from the list. You will still get a ConcurrentModificationException when you do this. Wrapping the list with Collections.synchronizedList(...) will make the individual methods on the list synchronized, but there is no synchronization over multiple methods.

You must make sure that if one thread is removing elements from the list, no other threads are iterating over the list, by properly synchronizing your own methods that iterate and remove elements.

Jesper
  • 202,709
  • 46
  • 318
  • 350
  • Thanks for your answer. So if I have two threads and in every of each there is an iterator(and at least one of them change the collection), I MUST to synchronize iteration(because if one make changes the other iterator will check modCount and Boom) ? And one more question - how enhance for is implemented that is throws exception, but the normal iterator doesn't. Thanks in advance. – DPM Dec 14 '14 at 23:29
  • 1
    Yes, if at least one of the threads is changing the collection, you need to synchronize over the entire loop. Second question: The trick is that you remove by calling `remove()` on the iterator instead of on the list itself - that way the iterator knows that the collection has changed. With an enhanced `for`-loop you don't have access to the iterator so you cannot call `remove()` on the iterator. – Jesper Dec 15 '14 at 08:11
1

for first question, yes. it is common exception when you modify the list if it is being enumerated.

for second question, yes. but you need to use synchronized block for your every for loops

Adem
  • 9,402
  • 9
  • 43
  • 58
1

Iterator is not Synchronized in SynchronizedCollection. I have attached SynchronizedCollection code.

static class SynchronizedCollection<E> implements Collection<E>, Serializable {
        private static final long serialVersionUID = 3053995032091335093L;

        final Collection<E> c;  // Backing Collection
        final Object mutex;     // Object on which to synchronize

        SynchronizedCollection(Collection<E> c) {
            this.c = Objects.requireNonNull(c);
            mutex = this;
        }

        SynchronizedCollection(Collection<E> c, Object mutex) {
            this.c = Objects.requireNonNull(c);
            this.mutex = Objects.requireNonNull(mutex);
        }

        public int size() {
            synchronized (mutex) {return c.size();}
        }
        public boolean isEmpty() {
            synchronized (mutex) {return c.isEmpty();}
        }
        public boolean contains(Object o) {
            synchronized (mutex) {return c.contains(o);}
        }
        public Object[] toArray() {
            synchronized (mutex) {return c.toArray();}
        }
        public <T> T[] toArray(T[] a) {
            synchronized (mutex) {return c.toArray(a);}
        }

        public Iterator<E> iterator() {
            return c.iterator(); // Must be manually synched by user!
        }

        public boolean add(E e) {
            synchronized (mutex) {return c.add(e);}
        }
        public boolean remove(Object o) {
            synchronized (mutex) {return c.remove(o);}
        }

        public boolean containsAll(Collection<?> coll) {
            synchronized (mutex) {return c.containsAll(coll);}
        }
        public boolean addAll(Collection<? extends E> coll) {
            synchronized (mutex) {return c.addAll(coll);}
        }
        public boolean removeAll(Collection<?> coll) {
            synchronized (mutex) {return c.removeAll(coll);}
        }
        public boolean retainAll(Collection<?> coll) {
            synchronized (mutex) {return c.retainAll(coll);}
        }
        public void clear() {
            synchronized (mutex) {c.clear();}
        }
        public String toString() {
            synchronized (mutex) {return c.toString();}
        }
        // Override default methods in Collection
        @Override
        public void forEach(Consumer<? super E> consumer) {
            synchronized (mutex) {c.forEach(consumer);}
        }
        @Override
        public boolean removeIf(Predicate<? super E> filter) {
            synchronized (mutex) {return c.removeIf(filter);}
        }
        @Override
        public Spliterator<E> spliterator() {
            return c.spliterator(); // Must be manually synched by user!
        }
        @Override
        public Stream<E> stream() {
            return c.stream(); // Must be manually synched by user!
        }
        @Override
        public Stream<E> parallelStream() {
            return c.parallelStream(); // Must be manually synched by user!
        }
        private void writeObject(ObjectOutputStream s) throws IOException {
            synchronized (mutex) {s.defaultWriteObject();}
        }
    }

As per document Iterator is SynchronizedCollection is not synchronised . So If iterate one thread another thread can add/delete. So ConcurrentModificationException will come.

Siva Kumar
  • 1,983
  • 3
  • 14
  • 26
  • 1
    This is more or less the same as what `Collections.synchronizedList(...)` does, and will not protect you from `ConcurrentModificationException` across method calls. – Jesper Dec 15 '14 at 13:45
  • @Jesper This is not more or less. This is synchronizedList code only – Siva Kumar Dec 15 '14 at 15:11
  • @Jesper is it help you – Siva Kumar Dec 16 '14 at 02:10
  • So if I have somewhere where I iterate over collection what is the reason to use synchronized collection ? I mean I have to synchronized this iteration by the collection(for example), but add, remove and other operations are synchronized by something else. So if I synchronized all of them manually(because to be synchronized by the same object) should I use this synchronized collections( which came with java) only if I don't have Iteration somewhere ? Thanks in advance. – DPM Dec 16 '14 at 02:19
  • @Jasper As per java doc it will like that only. You may use collection framework copyonwritearraylist. It will not throw ConcurrentModificationException. But it has performance issue – Siva Kumar Dec 16 '14 at 02:22