CSE190L Notes for Friday, 4/20/07

I spent some time demonstrating the colored rectangles program and answering questions. Then I said that I wanted to spend some time discussing Java collections. One goal of cse143 is that students will leave the class knowing how to use the standard collections classes effectively, but unfortunately, cse143 doesn't have the time to teach everything you need to know. So we spent some time discussing those issues.

I reminded people that many operations on collections are defined in terms of iterators. For example, if you construct an ArrayList, you can ask for an iterator like this:

        List<String> lst = new ArrayList<String>();
        lst.add("how");
        lst.add("are");
        lst.add("you");
        Iterator<String> iter = lst.iterator();
The primary operations on an iterator are:

I spent a fair amount of time discussing this paragraph from the api documentation for the ArrayList class:

The iterators returned by this class's iterator and listIterator methods are fail-fast: if 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.
Similar warnings appear in all of the major collections classes (HashMap, TreeMap, HashSet, TreeSet, LinkedList, etc). The idea is that you have to be careful about modifying a collection while you are iterating over it. What is an iterator supposed to do, for example, if you remove the value that it was about to return? What if you remove something later in the sequence? What if you add things to the end? All of these actions are likely to make it difficult for the iterator to do its job.

So the Java solution is to severely restrict the way that modifications can be done when you are using an iterator. If you modify the collection directly while an iterator is trying to iterate over the values, then it throws a ConcurrentModificationException, as in this code:

        while (iter.hasNext()) {
            String next = iter.next();
            System.out.println(next);
            lst.remove(0);
        }
The offending line of code here is the call on the list object's remove method. We saw that this generated the ConcurrentModificationException described in the Java documentation.

To do this sort of thing correctly, we have to call the iterator's remove method, as in:

        while (iter.hasNext()) {
            String next = iter.next();
            System.out.println(next);
            if (next.equals("how"))
                iter.remove();
        }
The collection can be modified while you iterate over it, but only if you do so with the iterator itself.

It is important to understand that some methods modify the state of an object and others do not. For example, we can call methods like size and get while iterating over an object and that does not cause a problem:

        while (iter.hasNext()) {
            String next = iter.next();
            System.out.println(next);
            System.out.println(lst.size());
            System.out.println(lst.get(0));
        }
These actions don't change the state of the object, so they don't generate an exception.

I spent a few minutes discussing how this is implemented. If you look closely at the ArrayList documentation, you'll see that it has a field called modCount. This is used to keep track of how many times the object has been modified. Each method like remove that modifies the state of the list increments this counter. Each iterator stores what the value that modCount had when it was constructed. It then compares the current value of modCount against the value it had when it was constructed to make sure that they match. If not, it throws an exception.

Then someone pointed out that it has to be a bit more complicated than that because the iterator itself can modify the object. If you call the iterator's remove method, then the mod count surely must go up. It will, but in that case the iterator would know to increment its own mod count. So the only time the iterator throws an exception is when its mod count doesn't match the collection's mod count, which would indicate that someone other than the iterator has performed a modification.

I then spent some time talking about how to use iterators with a map. I gave a more complete presentation in Monday's lecture, so I won't repeat those notes here.


Stuart Reges
Last modified: Wed Apr 25 11:45:32 PDT 2007