Welcome!

Java Authors: App Man, Liz McMillan, Jeremy Geelan, Yakov Fain, Hari Gottipati

Related Topics: Java

Java: Article

Java Design Patterns for Long Lists

Providing fast performance

In the late 1990s, a GUI design pattern emerged for choosing multiple objects from long lists. In GUI Design Essentials, Susan Weinschenk, Pamela Jamar, and Sarah Yeo called this the Selection Summary pattern. In "A Dual Listbox Selection Manager" by Steve Aube, it's also known as the Dual Listbox Selection interface. In The Java Look and Feel Guidelines, Advanced Topics, it is called the Add-and-Remove idiom.

The Add-and-Remove design pattern (shown in Figure 1) has many variations. One common enhancement is to provide "Move Up" and "Move Down" buttons to reorder the chosen list (see Figure 2). Sometimes the chosen list is displayed as a table to show additional information.

My previous article ("GUI Design Patterns," JDJ, Vol. 9, issue 7) showed how to optimize usability for this common GUI design pattern. This article shows how to optimize performance for this design pattern and other GUIs that display long lists.

Java Performance Patterns
For short lists, neither the JList or the JTable are likely to give performance problems; Java and Swing have been optimized many times over the years. If your lists contain thousands of objects though, there are some standard Java design patterns that optimize performance.

The key to all these performance patterns is to realize that the default list and table implementations are general purpose. To optimize performance, you want to bypass this general-purpose code by informing the JList or JTable of your application's specific needs.

Fix the Cell Size
For example, JLists by default assume that the objects they contain may be of varying sizes. If your application's objects are all the same size, you can inform the JList of this fact. It will then bypass its general-purpose, size-checking code. To fix the cell size, invoke either the setFixedHeight() and setFixedWidth() methods, or the setPrototypeCellValue() method. In "Advanced JList Programming," Hans Muller notes that setFixedHeight() and setFixedWidth() are useful to align a JList with another component. Otherwise, it's generally more convenient to use setPrototypeCellValue().

For the prototype cell value, use the value that is largest visually. If the maximum width is known, a prototype value can be assigned without looping over all the values, thus saving initialization time. Alternatively, if the application uses a monospace font, a fast loop can be written to check the string lengths of all values. Otherwise, to allow proportional fonts, anti-aliasing, and other issues, check the FontMetrics as in the code below.


   double width = 0;
   String prototype = "";
   FontMetrics fm = jList.getFontMetrics( jList.getFont());
   Graphics g = jList.getGraphics();
   for( int i = 0; ( i < values.length ); i++ )
   {  String s = values[ i ].toString();
      if( width < fm.getStringBounds( s, g ).getWidth())
      {  width = fm.getStringBounds( s, g ).getWidth();
         prototype = s;
      }
   }
   jList.setPrototypeCellValue( prototype );

Write a Custom Model
For many applications, fixing the cell size may provide all the performance boost you need. If it doesn't, the next step is to take advantage of Swing's flexible architecture.

Figures 3 and 4 show portions of the JList and JTable architectures. Both lists and tables allow you to replace their default models with custom models of your own. The only requirement is that your custom model implement the ListModel or TableModel interface.

The simplest way to do this is to extend AbstractListModel or AbstractTableModel. These classes provide management of listeners and events. Technically, to support the ListModel interface it is necessary to override only two methods in AbstractListModel:

public Object getElementAt( int i )
public int getSize()

Similarly, to support the TableModel interface it's necessary to override only three methods in AbstractTableModel:

public Object getValueAt( int row, int column )
public int getRowCount()
public int getColumnCount()

These methods support a ListModel or TableModel that is immutable: its contents can't be changed. For an application such as the Add-and-Remove pattern, the contents must be mutable. This requires extending the AbstractListModel with methods to add and remove values from the list:

public void addElement( Object o )
public void removeRElement( int i )

Similarly, the AbstractTableModel can be extended with methods to add and remove rows from the table.

public void addRow( Object[] row )
public void removeRow( int i )

Writing a custom model informs the Swing GUI control of your application's specific needs, causing it to bypass its general-purpose code. For example, both the DefaultListModel and the DefaultTableModel are Vector based. This means their accessor methods are synchronized. If your application doesn't require synchronization, it can be removed in the custom model, for example, by using an ArrayList instead of a Vector. A custom ListModel based on an ArrayList is shown in Listing 1.

For the Add-and-Remove pattern, a further performance boost can be obtained by realizing that the total number of objects never changes. Both Original and Chosen lists have a fixed maximum size, which is the sum of the number of objects in each. This means that a custom model does not need expandable storage, such as a java.util.Collection, so a more efficient array can be used instead. A custom TableModel based on an array is shown in Listing 2.

Finally, significant performance can be gained by adding methods to the custom model to process multiple objects. The custom ListModel of Listing 1 provides three methods to handle multiple objects:

public void addAll( Object[] objects )
public void clear()
public Object[] toArray()

In the DefaultListModel, to add 100 objects addElement() must be invoked 100 times. In the custom model, the addAll() method can be invoked only once. If an application operates on multiple objects, performance can almost always be improved by writing a custom model.

Can More Be Done?
For most applications, a custom model provides sufficient performance improvement. However, Swing provides another option using the same architectural pattern. As shown in Figures 5 and 6, JList and JTable rely on renderers to display their contents.

Just as the custom model replaced the default model, a custom renderer can replace the default renderer. The only requirement is that the custom renderer implement the ListCellRenderer or TableCellRenderer interface.

The performance boost from a custom renderer is roughly proportional to the number of objects being rendered. If your application displays only a few objects, as in the Add-and-Remove pattern, this performance boost is not significant. A better use of a custom renderer is given by Steve Wilson and Jeff Kesselman in Java Platform Performance: Strategies and Tactics. Their example displays a sparse table that derives a significant performance boost because the empty cells don't need to be rendered at all.

Some highly specialized applications require more specialized performance patterns. Hans Muller notes that internally JList uses the toString() method to convert objects to strings. If the application does not need this generality, the conversion time can be saved by building a custom model around the String class rather than the Object class.

In Christmas Tree Applications Scott Violet and Kathy Walrath give a fine and detailed example using a custom renderer and other performance patterns. Their code produces fast performance for frequently updated JTables. Patterns such as these are not usually needed, but they show the performance improvements that become possible when Swing is tailored to a specific application.

How Much Improvement Can We Expect?
Performance benchmarks for the Add-and-Remove pattern show that fixing the cell size is the most cost-effective performance pattern for JLists. Both JLists and JTables can achieve dramatic improvement from custom models, especially when processing multiple objects.

The benchmarks shown in Figures 7 and 8 and Tables 1-4 were run using JDK 1.4.1 under Mac OS X on a G4 CPU at 450MHz. Your mileage will vary, but these conclusions hold for most applications:

  • For a JList, fix the cell size.
  • For a long list or table, write a custom model.
  • For a specialized application, consider specialized design patterns such as a custom renderer.
Conclusion
The Add-and-Remove GUI design pattern enables users to choose multiple objects from long lists. Appropriate Java design patterns provide fast performance for this GUI and other applications.

Resources

  • Aube, S. (2000). "A Dual Listbox Selection Manager": www.codeguru.com/Cpp/controls/listbox/article.php/c4755
  • Muller, H. (2000). "Advanced JList Programming": java.sun.com/products/jfc/tsc/tech_topics/jlist_1/jlist.html
  • Sun Microsystems Inc. (2002). Java Look and Feel Design Guidelines: Advanced Topics. Addison-Wesley Professional: java.sun.com/products/jlf/at/book/Idioms6.html
  • Violet, S., and Walrath, K. (2002). "Christmas Tree Applications": java.sun.com/products/jfc/tsc/articles/ChristmasTree/
  • Weinschenk, S., Jamar, P., and Yeo, S. (1997). GUI Design Essentials. Wiley & Sons. p. 192, 206-207.
  • Wilson, S., and Kesselman, J. (2000). JAVA Platform Performance: Strategies and Tactics. Chapter 10: java.sun.com/developer/books/performance/
  • More Stories By Heman Robinson

    Heman Robinson is a senior developer with SAS Institute in Cary, N.C. He holds a BS in mathematics from the University of North Carolina and an MS in computer science from the University of Southern California. He has specialized in GUI design and development for 15 years and has been a Java developer since 1996.

    Comments (0)

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.