devxlogo

Charting Unknown Waters in JDK 1.4

Charting Unknown Waters in JDK 1.4

ondering what I would dig up in the JDK 1.4 source code, I recently started looking through the classes in the java.util.* package?without even the subpackages. That alone kept me busy for most of an afternoon, and although I have not finished exploring that J2SE release yet, I already have enough material for several articles. The following are some of the gems I discovered right away.

Did You Know toString() was Broken for Collections?
In the collection classes, all hell breaks loose when you try to call toString() on a collection that contains itself. 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!");      }    }  }

Running this program under JDK 1.3 or older versions generates a stack overflow. In JDK 1.4, however, the output is:

  {Heinz=Kabutz, all=(this Map), (this Map)=all, (this Map)=(this Map)}

This same principle applies to the other collections in JDK 1.4.

Is it a good idea to use a Hashtable as a key, you might ask. Dont forget what happens when the hash code of a key changes during its life (see 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. The question I have is why?despite the seemingly limitless amount of stupid code written every day?the people at Sun didn’t anticipate programmers adding a collection to itself.Introducing RandomAccess, A New Tag Interface
A highly paid software developer once wrote code along the lines of the following snippet. Take a moment to thoroughly read the code and understand it:

  /** @author Mr M.O.Nument */  import java.util.*;  public class ListSearching {    private List names = new LinkedList();    public void f() {      for (int i=0; iif (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;    }  }

Dont be too quick to judge the programmer. In his defense, a lot of code had to go between f() and size(), so the problem was not as obvious as it now appears. Also, the list was always very short, so performance was not an issue either.

JDK 1.4 introduces a new tag interface called RandomAccess (like java.io.Serializable) into the java.util package. It allows classes such as Collections to optimize their algorithms in the case where the following code:

  for (int i=0, n=list.size(); i < n; i++)    list.get(i);

runs faster than the following 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, because I have often found algorithms that I thought to be faster for linked lists actually being slower (see Issue 024 of The Java(tm) Specialists' Newsletter). The values of the thresholds are of course not configurable and are approximations based on empirical data (i.e., experiments) that would work well with the LinkedList.RuntimeException Specifications: An Admission of Guilt?
All over the JDK 1.4 java.util.* package, I have noticed that the RuntimeExceptions are now also specified in the @throws clause of the JavaDocs. Bruce Eckel and I exchanged quite a few e-mails debating the current exception model. We ended up wondering whether it perhaps was fundamentally flawed. For example, java.io.IOException has more than 30 subclasses that each 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 the following:

  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 continue to be encumbered by them? I think declaring as comments the runtime exceptions that a method could throw is a step in the right direction.

More to Come
Look for other interesting little snippets I've discovered in my upcoming articles.

Editor's Note: This article first appeared in Dr. Kabutz's The Java(tm) Specialists' Newsletter under the title Charting Unknown Waters in JDK 1.4 Part I. To subscribe, send an email to [email protected].

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist