|
The Java Specialists' Newsletter
Issue 040 2002-01-31
Category:
Language
Java version: Visiting your Collection's Elementsby Dr. Heinz M. Kabutz
Welcome to the 40th edition of "The Java(tm) Specialists'
Newsletter", sent to over 2550 Java experts in over 70 countries.
Despite my plea that you don't unsubscribe, I had a rather
surprising number of unsubscriptions, as programmers expressed
their outrage at my audacity by voting with their feet. My views
are my own and that of my employer - since I am my own
employer ;-). I'm working on a program at the moment and I do make
sure that our JavaDocs are up to date by running a Doclet that
tells me where I've forgotten a tag. Whenever I change a method,
I nuke the comments, and then the Doclet tells me where I need to
add a comment again.
The ideas in this newsletter were spawned by Inigo Surguy
(inigosurguy@hotmail.com)
who works in Lemington Spa in the United Kingdom. Inigo is the
UK Head of Research and Development of Interactive AG. Inigo also
pointed out BCEL to me, used to change byte code "on the fly". I
will write about some application of that in future.
A few newsletters ago, I mentioned traffic fines, and that I had
had only one speeding fine in all my life. Last Wednesday, I was
on my way to a meeting with my auditor, I was late, and, hmmm,
make that 2 traffic fines in all my life? The road between where
I live and where my auditor works is notorious. The police tell
you: "if you have a puncture on that road, please carry on
driving slowly until you get to the next town. Don't worry about
damaging your wheel - rather break your wheel than ..." Ok, I'm
exaggerating a bit, but the point I'm making is that I had never
seen a speed trap on that road, because the cops are too scared
to hang around long enough to book you. Never, until last
Wednesday. I was caught fair & square, doing 160km/h in a
120km/h zone. Fortunately, the cop was in a good mood, so we had
a good laugh when he pulled over some cops who were speeding, and
he kindly reduced my speed to 139km/h. The speeding fine ended
up being ZAR 100, about US$ 8.50. I'd be quite interested to
hear from you what type of punishment you would face in your
country for getting caught doing 160km/h in a 120km/h zone ...
[hk: in case there are any cops on this list, that story was
purely ficticious :-]
Would you like to really understand Java concurrency? Join us for an
in-depth study of how threading works in Java. During the course,
you will learn how to write correct and fast multi-threaded Java code.
Please
click here if you would like to learn more. Visiting your Collection's Elements
The Problem
I'm getting tired. Not tired of writing newsletters, but tired
of Java. Tired of writing the same code over and over again.
For example:
// ...
Iterator it = ages.iterator();
while(it.hasNext()) {
Integer age = (Integer)it.next();
System.out.println("Now you're " + age +
", in 3 years time, you'll be " + (age.intValue() + 3));
}
I don't like that while loop with the iterator.
Don't know why I don't like it, it just looks inelegant to
me. I like the weird for loop for an iterator even
less:
// ...
for(Iterator it = ages.iterator(); it.hasNext();) {
Integer age = (Integer)it.next();
System.out.println("Now you're " + age +
", in 3 years time, you'll be " + (age.intValue() + 3));
}
Lastly, I don't like downcasting and I don't like the problems
that occur when you have different types in a collection.
A different way ...
Before looking at a solution, I would like to show how I would
use iterators normally:
import java.util.*;
public class OldVisitingIteratorTest {
public static void main(String[] args) {
Collection c = new LinkedList();
for (int i=0; i<3; i++) c.add(new Integer(i));
Iterator it = c.iterator();
while(it.hasNext()) {
// lots of brackets - looks almost like Lisp - argh
System.out.println(((Integer)it.next()).intValue() + 10);
}
c.add(new Float(2.1));
c.add("Hello");
it = c.iterator();
while(it.hasNext()) {
Object o = it.next();
if (o instanceof Integer) {
System.out.println(((Integer)o).intValue() + 10);
} else if (o instanceof Number) {
System.out.println(((Number)o).intValue() + 20);
} else if (o instanceof String) {
System.out.println(((String)o).toLowerCase());
} else {
System.out.println(o);
}
}
it = c.iterator();
while(it.hasNext()) {
System.out.println(((Integer)it.next()).intValue() + 10);
}
}
}
The output from that code is:
10
11
12
10
11
12
22
hello
10
11
12
Exception in thread "main" java.lang.ClassCastException: java.lang.Float
at OldVisitingIteratorTest.main(OldVisitingIteratorTest.java:32)
Instead of constructing an Iterator and going through the
Iterator and doing some operation on its contents, why not pass
in an object with an execute() method that is called with each
element? After some speed-typing yesterday, while waiting for my
students at a Design Patterns course at the Strand Beach Hotel
near Cape Town to finish an exercise, I came up with:
import java.util.*;
import java.lang.reflect.*;
public class VisitingIterator {
/**
* Ordering methods in "best-fit" order.
*/
private static final Comparator METHOD_COMPARATOR =
new Comparator() {
public int compare(Object o1, Object o2) {
Class paramType1 = ((Method)o1).getParameterTypes()[0];
Class paramType2 = ((Method)o2).getParameterTypes()[0];
return paramType1.isAssignableFrom(paramType2) ? 1 : -1;
}
};
/**
* Threadsafe version of visit.
* @param lock the object on which to synchronize
* @param task is an Object with an execute(...) : void method
*/
public void visit(Collection c, Object task, Object lock) {
synchronized(lock) {
visit(c, task);
}
}
/**
* @param task is an Object with an execute(...) : void method
*/
public void visit(Collection c, Object task) {
TreeSet methods = new TreeSet(METHOD_COMPARATOR);
Method[] ms = task.getClass().getMethods();
for (int i=0; i<ms.length; i++) {
if (ms[i].getName().equals("execute")
&& ms[i].getParameterTypes().length == 1) {
methods.add(ms[i]);
}
}
Iterator it = c.iterator();
while(it.hasNext()) {
boolean found = false;
Object o = it.next();
Iterator mit = methods.iterator();
while(!found && mit.hasNext()) {
Method m = (Method)mit.next();
if (m.getParameterTypes()[0].isInstance(o)) {
try {
m.invoke(task, new Object[] { o });
} catch(IllegalAccessException ex) {
// we were only looking for public methods anyway
throw new IllegalStateException();
} catch(InvocationTargetException ex) {
// The only exceptions we allow to be thrown from
// execute are RuntimeException subclases
throw (RuntimeException)ex.getTargetException();
}
found = true;
}
}
if (!found)
throw new IllegalArgumentException(
"No handler found for object type " +
o.getClass().getName());
}
}
}
Instead of having that ugly while loop, we can now
pass an object to the VisitingIterator and the correct
execute(...) method is called for each element
in the collection. The OldVisitingIterator now becomes:
import java.util.*;
public class VisitingIteratorTest {
public static void main(String[] args) {
Collection c = new LinkedList();
for (int i=0; i<3; i++) c.add(new Integer(i));
VisitingIterator vit = new VisitingIterator();
vit.visit(c, new Object() {
public void execute(Integer i) {
System.out.println(i.intValue() + 10);
}
});
c.add(new Float(2.1));
c.add("Hello");
vit.visit(c, new Object() {
public void execute(Object o) {
System.out.println(o);
}
public void execute(Number n) {
System.out.println(n.intValue() + 20);
}
public void execute(Integer i) {
System.out.println(i.intValue() + 10);
}
public void execute(String s) {
System.out.println(s.toLowerCase());
}
});
vit.visit(c, new Object() {
public void execute(Integer i) {
System.out.println(i.intValue() + 10);
}
});
}
}
The output from our new style is:
10
11
12
10
11
12
22
hello
10
11
12
Exception in thread "main" java.lang.IllegalArgumentException:
No handler found for object type java.lang.Float
at VisitingIterator.visit(VisitingIterator.java:62)
at VisitingIteratorTest.main(VisitingIteratorTest.java:33)
Perhaps I've been smoking Java for too long, but I much prefer
that code to the while(it.hasNext()) ... but I have
not had the chance to try this idea out "in the real world". I
will start using it and let you know if it makes code neater
(or not). I know that it will be less efficient, but then, Java
is so slow anyway, I'd rather have cool style than super-optimal
code.
Until next week ...
Heinz
Language Articles
Related Java Course
Discuss at The Java Specialist Club
|