|
SYS-CON.TV Webcasts
Comments
Did you read today's front page stories & breaking news?
SYS-CON.TV
|
Top Links You Must Click On
General Java Java Design Patterns for Long Lists
Providing fast performance
By: Heman Robinson
Aug. 5, 2004 12:00 AM
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 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 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 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 ) Similarly, to support the TableModel interface it's necessary to override only three methods in AbstractTableModel: public Object getValueAt( int row, int column ) 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 ) Similarly, the AbstractTableModel can be extended with methods to add and remove rows from the table. public void addRow( Object[] row ) 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 ) 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? 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? 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:
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 Reader Feedback: Page 1 of 1
Enterprise Open Source Magazine Latest Stories . . .
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
|
SYS-CON Featured Whitepapers
Most Read This Week |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||