Thursday, February 26, 2009

Funny error with Java’s extended for-loop

Java 5 introduced the enhanced for-loop – or for-each loop – which simplifies iterating over many types of collections. Have a look at this simple piece of code and tell me what’s wrong:

package de.danielschneller.blog.iterator;

import java.util.HashSet;
import java.util.Set;

public class Demo {
	public static void main(String[] args) {
		Set set = new HashSet();
		set.add("Value 1");
		set.add("Value 2");
		set.add("Value 3");
		set.add("Value 4");
		set.add("Value 5");
		
		synchronized (set) {
			for (String tEntry : set) {
				if (tEntry.endsWith("3")) {
					System.out.println("3 has to go");
					set.remove(tEntry);
				} else {
					System.out.println(tEntry + " may stay");
				}
			}	
		}
	}
}

When I wrote this I naively expected something like this to be written out(the order of the iteration is undefined for a HashSet, but bear with me:

Value 5 may stay
Value 2 may stay
3 has to go
Value 4 may stay
Value 1 may stay

Admit it, this is what you would have guessed, too! But instead this code throws an exception! The problem occurs in line 16 and looks like this:

Value 5 may stay
3 has to go
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:841)
at java.util.HashMap$KeyIterator.next(HashMap.java:877)
at de.danielschneller.blog.iterator.Demo.main(Demo.java:16)

Any clearer now? Well, turns out that the hidden magic behind some of the Java 5 features (this time the part that makes the enhanced for-loop possible) promotes  some kinds of problems I – in this case – would most probably have seen and avoided had I used the old-style Iterator approach.

Under the hood there still is an Iterator at work, but the syntactic sugar in the example above hides this detail from you. At compile time the above will be translated into the old-style iteration for you.

Because now there is no obvious Iterator instance, it is easy to forget this important part of the JDK API documentation for “ConcurrentModificationException”:

Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will thow [sic] this exception

This is exactly what happens here. The Iterator is still there, even if it does not appear in the source. It’s hasNext() and next() methods are used to loop through the elements of the collection. Once “Value 3” has been found, the code removes it from the Set directly, bypassing the Iterator. The next time it is used – which is when the next element is prepared to be passed into the loop body – it detects that the underlying collection has been independently changed and obeys its fail-fast policy, producing the exception above.

So in this case, even without the classical problem of multiple threads sharing mutable state, there are two parties modifying the collection. To solve the problem, we have to fall back to the classical looping code:

String tEntry;
for (Iterator iter = set.iterator(); iter.hasNext(); ) {
	tEntry = iter.next();
	if (tEntry.endsWith("3")) {
		System.out.println("3 has to go");
		iter.remove();
	} else {
		System.out.println(tEntry + " may stay");
	}
}

This in fact produces the desired output, however the code looks somewhat more cluttered than before.

Proves (again) that despite lots of eye-candy, syntactic sugar or whatever it is called you still need to understand what is going on under the covers most of the time. A novice programmer might get really confused about this...

Technorati Tags: ,,

Monday, February 02, 2009

Disable drop shadow on OS X screen shots

Recently I found myself doing numerous screenshots on OS X. While the drop-shadow effect that gets included in the resulting PNG files looks nice on screen, it tends to come out rather bad on black and white printouts. As there is no GUI setting for this one has to use the "defaults" command on a terminal:

defaults write com.apple.screencapture disable-shadow -bool true
killall SystemUIServer

Instead of killing the SystemUIServer process you could also log out and back in. To restore the shadow, just delete this property again:

defaults delete com.apple.screencapture disable-shadow
killall SystemUIServer

Oh, and just in case you did not know: These are the keyboard shortcuts to take screen shots in the first place:

  • Fullscreen: Shift-Cmd-3
  • Rahmen: Shift-Cmd-4
  • Fenster: Shift-Cmd-4, Space
Technorati Tags: ,