|
The Java Specialists' Newsletter
Issue 053 2002-07-23
Category:
Performance
Java version: Charting unknown waters in JDK 1.4 Part Iby Dr. Heinz M. Kabutz
Welcome to the 53rd edition of The Java(tm) Specialists' Newsletter sent to over 4100
Java Specialists in 85 countries.
Our friendly archive host Carl
Smotricz has been experiencing some problems with his
service provider, so our archive was offline for a bit.
In the meantime, I have added my own archive to my website,
so in future please look at our website
for old issues.
Ahh, the pressures of life. This month has been hectically busy.
The first week I gave some Java training at the University of Stellenbosch
IT department to teach their Natural programmers Java. Very
enjoyable week. The second week was spent presenting a Java
2 Standard Edition course and last week we moved in to our new
house. I'm now sitting in my new office at home overlooking
False Bay, and wondering what gems I can dig up in the source
code of the JDK 1.4 ;-) Please arrange to visit me if you
ever come to the Helderberg, a village about 50km outside of
Cape Town.
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. Charting unknown waters in JDK 1.4 Part I
As I said in my opener to this newsletter, I started by just looking
at the java.util.* package, without even the subpackages, and that kept
me busy for most of the afternoon. I have not finished yet, but I
already have enough material for several newsletters.
toString() was broken - did you know that?
In the collection classes all hell breaks loose when a collection contains itself
and you try to call toString() on it. For example,
consider the following code:
import java.util.Hashtable;
public class HashtableTest {
public static void main(String[] args) {
Hashtable ht = new Hashtable();
ht.put("Heinz", "Kabutz");
ht.put(ht, "all");
ht.put("all", ht);
ht.put(ht, ht);
try {
System.out.println(ht);
} catch (StackOverflowError e) {
System.out.println("Caused Stack Overflow Error!");
}
}
}
Under the JDK 1.3 and before, when you ran this program, a Stack
Overflow was generated, however, in JDK 1.4, the output is:
{Heinz=Kabutz, all=(this Map), (this Map)=all, (this Map)=(this Map)}
This same principle applies to the other collections. Is it a good
idea to use a Hashtable as a key? Remember what happens when the
hash code of a key changes during its life? I discussed that at
length in Issue 031 of The Java(tm) Specialists' Newsletter.
Each time a Hashtable changes, its hash code changes
as well, so you should not use it as a key. However, there is no
limit to the amount of stupid code that gets written every day, and
the chaps at Sun didn't expect people to add a collection to itself.
RandomAccess
A highly paid software developer once wrote something along these lines:
/** @author Mr M.O.Nument */
import java.util.*;
public class ListSearching {
private List names = new LinkedList();
public void f() {
for (int i=0; i<size(); i++) {
if (names.get(i) == "Heinz")
System.out.println("Found it");
}
}
private int size() {
int result = 0;
Iterator it = names.iterator();
while(it.hasNext()) {
result++;
it.next();
}
return result;
}
}
Take a moment to read through the code and try to understand it. In
defense of the programmer, firstly he had not been on my Java
course ;-), secondly there was a lot of code inbetween f() and size() so
the problem was not as obvious as we can now see and thirdly the list
was always very short so performance was not an issue either.
In JDK 1.4, a new tag interface (like java.io.Serializable) was introduced
into the java.util package, called RandomAccess. This allows
classes such as Collections to optimize their algorithms in
the case where:
for (int i=0, n=list.size(); i < n; i++)
list.get(i);
runs faster than this loop:
for (Iterator i=list.iterator(); i.hasNext(); )
i.next();
What I found interesting was that the algorithms always treat the
collections as RandomAccess unless they are bigger
than some threshold. This makes a lot of sense to me, because I
have often found algorithms that we thought to be faster for linked
lists actually being slower, as I discussed in Issue 024 of The Java(tm) Specialists' Newsletter. The values of the
thresholds are of course not
configurable and look like empirical thumbsuck that would work well
with the LinkedList.
RuntimeException Specifications - Admission of Guilt?
All over the JDK 1.4 java.util.* package, I have seen that the
RuntimeExceptions are now also specified in the @throws
clause of the JavaDocs. Bruce
Eckel and I spent quite a few emails debating the current
exception model, and we ended up wondering whether it was not perhaps
fundamentally flawed. For example, java.io.IOException has over 30
subclasses that could be the actual exception being thrown. What good
is that? When you see that a method throws IOException, what is actually
being thrown, and how do you deal with it?
I think that checked exceptions should be scrapped.
They don't work. They cause bad code, such as:
while(true) {
try {
Thread.sleep(1000);
// do some other work
} catch (Exception e) {}
}
Checked exceptions are responsible for more sloppy code and bugs than
any other construct in the Java language. C# does not have checked
exceptions, neither does C++. Why does Java have to be encumbered by
them?
I think that declaring as comments what runtime exceptions could possibly
be thrown by a method is a step in the right direction.
I've discovered some other interesting little snippets, which I will
share with you in my next newsletter.
Until the next newsletter...
Heinz
Performance Articles
Related Java Course
Discuss at The Java Specialist Club
|