Now blogging at diego's weblog. See you over there!

a JList "feature"

Yesterday I was doing performance testing on pro when I hit upon something strange: deleting an email from the mail view was taking too long.

What was even more strange was that this only happened when using the Delete key, and not when using the toolbar icon (strange since essentially the process is the same in both cases). This pointed so something with the keys--but what?

After a bit of debugging I discovered that the slowdown was happening on the repaint thread, which made it more complicated to debug since it happened outside of my control, so to speak. More testing.

Backtracking for a second: JLists can be used in "dynamic" mode: when you set the appropriate parameters the list will only load the items that are visible in the viewport, which is critical when there's access to disk-based data involved. So generally any kind of list activity implies only a few hits on the DB, say 10-20, which happens in a few milliseconds.

So I eventually placed a print statement on the getElementAt call of the ListModel and discovered the problem: when using the Delete key to delete an item, the entire contents of the ListModel where scanned, twice. On a typical email view this meant loading from the DB thousands of objects. Caching was kicking in, of course, but memory usage went through the roof. That, and the time it was taking was not acceptable. Not good.

But why was this happening? I checked and double checked the code and I couldn't find what could possibly be triggering a full reload of the list (twice!). Finally, I started checking the call traces on the getElementAt and I discovered that there's an inner class called KeyHandler inside BasicListUI (that handles the underlying UI for JList) that was doing something strange in its keyPressed method.

So what was it doing? The javadocs for that method say the following:

Moves the keyboard focus to the first element whose first letter matches the alphanumeric key pressed by the user. Subsequent same key presses move the keyboard focus to the next object that starts with the same letter.
When I read this, all I could think of was: Oh. My. God.

The problem, of course is that to determine whether "the next object starts with the same letter" or not it needs to obtain the String for that cell. This means getting the String if the contents are a String, or doing a toString() on the object.

Skipping for a moment on why on earth you'd want complex behavior like this pre-built... what if you're using the JList for an arbitrary component that doesn't translate into a String? What if the toString() is meaningless? Then the listener iterates through the entire list and, of course, fails to lock on to what it was looking for. And when the list is in the process of changing, it does it twice.

Oh yeah, it's a great "feature."

Even worse, the keyPressed call doesn't check for actual alphanumeric keys being pressed. Any key that is not a function key, cursors, or not registered through KeyStrokes (such as Page Up) goes through this loop.

Even if you're not hitting this problem head on as I was, it's strange to think that people that need this behavior would rely on a completely generic implementation that doesn't take into account the underlying storage mechanism.

So how to disable it? Since this "really helpful" listener is registered automatically on any list created, the only way I've found of disabling it is removing it "by hand" right after the list is created, as follows:

JList list = new JList();
KeyListener[] keyListeners = list.getKeyListeners();

for (int i=0; i list.removeKeyListener(keyListeners[i]);
By the way, this is the only listener that is pre-registered (I checked) so doing the loop actually just removes the listener in question. It's ugly, but it works.

This leaves me wondering what other "features" are lurking in there. It's pretty bad that something like this is set as default behavior with no warning, on the other hand it's good that once I identified the problem I could fix relatively easily even if the solution is less than ideal. In any case, it was an interesting (if at times maddening) trip into the deep core of Swing.

So if you're wondering why key presses are slowing down your JList implementation, it's quite possible this is the reason why.

Posted by diego on July 11 2004 at 6:01 PM

Copyright © Diego Doval 2002-2011.
Powered by
Movable Type 4.37