Christmas Tree Applications

   

Christmas Tree Applications
How to Create Frequently-Updated JTables that Perform Well

By Scott Violet & Kathy Walrath

One common type of application features a JTable with frequently updated data. This style of application is often found in the financial industry, but it can crop up in other industries, as well. This article explores techniques for improving the performance of this style of application, affectionately called Christmas tree applications because the rapid updating of their GUIs resembles blinking lights on holiday trees.

Background

Because Swing is a general purpose toolkit, many of its design decisions were made in the name of flexibility and extensibility. When you know what your application is doing, however, and know that you do not require the level of flexibility offered by Swing, you can make many optimizations to speed things up. Some of these performance improvements come at a price: loss of flexibility. This isn't an issue in the sample application that goes along with this article.

Understand Your Application

Before you begin incorporating any of the suggestions from this article into your application, you need to thoroughly understand your application. If it takes your application 15 seconds to figure out what needs to be updated, speeding up the painting code by a factor of 10, or even 100, likely won't make much of a difference to the user. To this end we highly recommend that you profile your application without the painting code to make sure the data side of your application performs reasonably well.

Once you're confident in your non-painting code, you can profile the application with painting code. We used Optimizeit to gather information about where our sample application's time was spent. We found many unnecessary repaint requests and discovered we could eliminate some unnecessary code and object creation from commonly executed methods. We also found that we could streamline and speed up the publishing and processing of table model events, which inform the JTable that the table's data has changed.

Goal

Our goal should be obvious, but it is worth stating because it will affect some of the decisions that we make later on: the application should be responsive to the user.

The Application

This article comes with a sample Christmas tree application, which looks like this:

[an animated gif showing how the application's display changes constantly]
Click the image to see a full-size screenshot.

Source code for our sample application is available under BSD+ license. All the source and build files are in XmasTreeSource.zip . The source code consists of the following classes:

  • GeneratorThread - Subclass of Thread that is responsible for accumulating changes. The thread sleeps for a defined period and then generates a set of changes to be published later. This behavior simulates a real application where a thread is talking to some continually updating data feed.
  • UpdateThread - Subclass of Thread that is responsible for taking a set of changes generated by GeneratorThread and publishing the changes to the model.
  • CTTableModel - Custom TableModel that models a Set of Datas. It is implemented as a List of Lists. Additionally, this model uses a Map to facilitate finding the row of individual data items.
  • Data - Represents the data of the application. In our implementation, Data objects contain no real data.
  • DataChange - Consists of a Data and a column number. GeneratorThread generates a Map of DataChanges that map from DataChange to a new value.
  • CTTable - JTable subclass that implements the techniques outlined in this article.
  • CTTableCellRenderer - Subclass of DefaultTableCellRenderer that implements the techniques outlined in this article.
  • VisibleTableModelEvent - Subclass of TableModelEvent used to avoid repainting regions that aren't visible.
  • Main - Ties everything together and builds the GUI.

The sample application allows you to configure the following options, either by modifying Main.java or through text fields in the GUI:

 

JTable Painting

JTable, like JTree and JList, uses renderers to paint individual cells. Each renderer wraps a Component (which we'll call the rendering component) and can be thought of as a rubber stamp: It is moved to the region of the affected cell and painted. Painting a JTable consists of two parts -- determining the cells to be painted, based on the clip region of the Graphics object passed to paint, and then painting each cell. For each cell that needs to be painted, the following steps occur:

  1. Getting the renderer by invoking getCellRenderer.
  2. Getting the value from the model.
  3. Getting the rendering component.
  4. Adding this component to the CellRendererPane and painting the CellRendererPane. ( CellRendererPane is a convenience class used to paint the rendering component. We'll discuss it in more detail later.)

Let's look for potential performance improvements in painting individual cells.

Step 1: Getting the renderer

The JTable implementation of the getCellRenderer method retrieves table cell renderers in the following way:

  • The method checks whether a renderer is associated with the table column. If so, the method returns that renderer.
  • If not, the method returns the renderer most appropriate for the Class of the Object returned from the model.

Custom renderers such as ours are often registered using the latter, Class-based approach because of its flexibility. Unfortunately, to obtain the renderer the JTable must do a lookup in a Map. Using a Map is typically a cheap operation, but because the lookup is performed for every cell that is painted we'll avoid this approach.

The alternate approach (associating a renderer with a table column) is relatively cheap and provides quite a bit of flexibility. But we can do better!

Because this application uses the same renderer for all cells, CTTable overrides getCellRenderer to simply return the renderer. This is the fastest approach possible as no additional lookup is done to obtain the renderer.

Step 2: Getting the value from the model

Because CTTableModel is implemented as a List of Lists, getting the value is a pretty cheap operation. If you decide to implement a custom TableModel, be sure to make getting the value a cheap operation, and avoid synchronization if possible.

Step 3: Getting the rendering component

Our custom table cell renderer, CTTableCellRenderer, is a subclass of DefaultTableCellRenderer. Like its parent, our renderer responds to the getTableCellRendererComponent method by updating its own state (to reflect the desired appearance for the cell being rendered) and then returning itself.

To improve performance, DefaultTableCellRenderer overrides a handful of methods to be more efficient -- often, to do nothing. If you create custom TableCellRenderers not derived from DefaultTableCellRenderer, you should override these methods as well. Refer to the API documentation for DefaultTableCellRenderer for a list of these methods. Note that the override of isOpaque, added in 1.4, is appropriate for table cell renderers written for earlier versions, as well.

Our custom renderer overrides and simplifies two of the methods already overridden by DefaultTableCellRenderer. We override the firePropertyChange method to do nothing at all, since this application does not show HTML text in the table. ( DefaultTableCellRenderer overrides firePropertyChange to only notify listeners when the "text" property changes, which is required for HTML support.) Our renderer also overrides the isOpaque method to be simpler than the DefaultTableCellRenderer implementation. Details of its implementation are discussed later in this article.

Two additional methods are overridden by CTTableCellRenderer to do nothing: the no-argument repaint method and invalidate. The first, repaint, can be overridden because the renderer is only transient; any repaints do not need to processed later. Additionally we can override invalidate safely because our rendering component does not contain child Components and does not rely on invalidate to reset any internal state. We expect DefaultTableCellRenderer to override the no-argument repaint method in a future release.

One key thing to remember is that your TableCellRenderer should do as little as possible in preparing the component to be used for rendering: set the text and colors, and that should be it. Because the rendering component is fetched for every cell, any additional operations tend to balloon in cost. For example, it is often necessary to support tooltip text in the cells of a JTable, but setting the tooltip text on each cell is an expensive operation. Instead, override getToolTipText in a JTable subclass.

Step 4: Painting the CellRendererPane

Most of the painting for any JComponent is handled by the component's UI object. In most look and feels, the UI object for a JTable descends from the BasicTableUI class. CellRendererPane is a convenience class used by BasicTableUI to paint the rendering component.

In step 4, the rendering component is added to the CellRendererPane and the UI asks the CellRendererPane to paint. Having the CellRendererPane might seem unnecessary, but it allows a rendering-savvy component to always be parented to the JTable, rather than having to add it on every paint. (The CellRendererPane is not visible.) Having the CellRendererPane also gives us a single place to override as no-ops a number of methods that typically walk the containment hierarchy (such as invalidate).

Our application uses a custom CellRendererPane ( CTTable.CellRendererPane) to avoid duplicating the Graphics object passed into its painting method. Normally, Swing components clone the Graphics object before passing it to children, so that any changes made by the child can't clobber the Graphics object used by the parent. Clobbering the Graphics object means setting a property on it, such as XORMode, that doesn't later get reset by either the child's code or Swing's code. Because we know that our painting code doesn't clobber the Graphics object, we can skip the costly step of cloning it.

The trick with having a custom CellRendererPane subclass is installing it. Because the CellRendererPane is managed by the UI ( BasicTableUI), we need to create a subclass of BasicTableUI that replaces the default renderer with our custom CellRendererPane.

Warning: Subclassing UIs, while possible, locks you into a particular look and feel. In our example this isn't an issue, but it might be in your application.

To force a subclass of JTable to always have a particular UI, override the updateUI method:

    public void updateUI() {
        super.updateUI();
        setUI(new CTTableUI());
    }

Our subclass of BasicTableUI replaces the CellRendererPane using the following code:

Option Description
Columns Number of columns the table is to contain.
Rows Number of rows the table is to contain.
Update Sleep Amount of time, in milliseconds, to sleep between runs of UpdateThread.
EQ Sleep Amount of time, in milliseconds, UpdateThread should sleep (if the event queue is busy) before trying to update the model.
Update All Threshold  Number of cells that need to change before a table model event is fired indicating the complete table has changed.
Generator Sleep Amount of time GeneratorThread sleeps between updates.
Batch Size Number of cells GeneratorThread is to update.
    class CTTableUI extends BasicTableUI {
        public void installUI(JComponent c) {
            super.installUI(c);
            c.remove(rendererPane);
            rendererPane = new CTCellRendererPane();
            c.add(rendererPane);
        }
    }

Optimizing Painting in the Rendering Component

Having a custom renderer ( CTTableCellRenderer) lets us improve the code that's executed every time a table cell is painted. Our improvements center around avoiding unnecessary object creation and method calls, in general, and streamlining how we deal with the background color, in particular.

One small improvement we made was having CTTableCellRenderer override setBackground and getBackground to cache the background color. ( DefaultTableCellRenderer changes the background color on every invocation of getTableCellRendererComponent.) Although getting and setting the background are typically cheap operations, removing unnecessary method calls can't hurt.

We also avoid unnecessary painting of the background. CTTableCellRenderer does this by overriding the isOpaque method to return true only when the background color of the cell differs from that of the JTable. This works because the default JComponent painting code (in ComponentUI.update) fills in the component's background only if the component is opaque. When the cell's background color matches that of the table, we can rely on the table's painting code to fill in the background.

To understand how we simplified the painting code, in general, you need to understand what happens when a JComponent gets a paint request. When a JComponent is asked to paint itself, its paint method invokes its paintComponent, paintBorder, and paintChildren methods. The paint method also checks the clipping rectangle and performs other operations to attempt to improve painting performance.

For our rendering component, only the code in paintComponent is necessary. The rest is irrelevant because our component is small, paints quickly, doesn't use a Border, and has no child components to paint. Thus, we can save a method call by moving the painting code from paintComponent into the paint method.

The default paintComponent implementation clones the Graphics passed into it and invokes update on the UI to handle the actual painting. As we mentioned before, our application will not clobber the Graphics during painting. We can therefore safely skip cloning the Graphics object, which lets us avoid the creation of an additional Graphics object per cell during painting. We can thus override the paint method so that it simply invokes the UI's update method. The CTTableCellRenderer version of paint ends up looking like this:

    public void paint(Graphics g) {
        ui.update(g, this);
    }

Notifying the Table of Data Changes

Up to this point, we've discussed various options for reducing the overhead of painting, but we haven't delved into notifying the table of updates. We'll briefly talk about this now.

First, you need to understand how the table model and table are notified of data changes. When the data represented by the TableModel changes -- for example, you change a cell's value ( DefaultTableModel.setValueAt) or add a row ( DefaultTableModel.addRow) -- the table model sends a TableModelEvent to all TableModelListeners registered on the model, notifying them of the change. Each JTable installs itself as a listener on its table model so that the table can update its display in response to model changes.

Most Christmas tree applications have a thread that is responsible for reading the data to be displayed in the table. Having this separate thread is a good thing as it does not lock down the event-dispatching thread in any way. The UpdateThread in our application takes the following approach in notifying the table model of changes:

  1. Accumulate some changes to apply.

     

  2. When the table is not scrolling and the event-dispatching thread has no pending events, inform the table model of all the changes.

    We notify the table model by executing a loop on the event-dispatching thread that calls the table model's set(row, column, value, notifyFlag) method (a non-standard method we added to our table model) once for each cell whose value has changed. We'll discuss the notifyFlag parameter later.

     

  3. To allow the user time to interact with the application, sleep for some amount of time before returning to 1.

Publishing Changes

UpdateThread uses invokeAndWait to schedule a Runnable to update the table model. To avoid scheduling more Runnables than the system can keep up with, thereby causing the application to be dead in the water, the calling thread needs to block until it knows the Runnable has been processed; invokeAndWait does just this. We can't use invokeLater because it doesn't block the caller, with the result that more Runnables might be scheduled than could be processed.

After the model changes an event is published that ultimately makes its way to the JTable and results in a repaint request for the affected cell or cells. Because such a large portion of the table can be updated at once, notifying listeners of the model change and making the subsequent repaint request need to have as little overhead as possible.

JTable responds to table model events by invoking repaint for the region identified by the TableModelEvent. As only a small portion of the JTable is visible at a time, the majority of the updated regions are not visible, meaning much of the work done by JTable to process the TableModelListener method notification is unnecessary, as well as costly.

To avoid unnecessary work but continue to notify listeners, we've created a custom subclass of TableModelEvent that CTTableModel uses when handling changes from UpdateThread. The new subclass, called VisibleTableModelEvent, adds the method isVisible(JTable), which calculates whether the rows and columns specified by the event are visible in the passed-in table. CTTable overrides tableChanged (its method that processes TableModelEvents) to ignore VisibleTableModelEvents for non-visible cells. VisibleTableModelEvent is mutable so that CTTableModel can reuse a single instance of VisibleTableModelEvent. This allows for minimal garbage creation when the table is modified.

Warning: Having mutable event objects could cause problems if consumers cache the TableModelEvents. This typically is not an issue, however -- especially internally in Swing.

One additional point worth mentioning is that sometimes the number of changes is close to that of the underlying model -- most or all cells have changed. In this case, it is far cheaper to fire a single event indicating all the cells have changed (using TableModel.fireTableDataChanged) than for the model to send an update for each cell. In our application, UpdateThread checks the number of changes before it enters the loop that invokes set(row, column, value, notifyFlag) on the table model. If the number of changes reaches some threshold, UpdateThread prevents the table model from firing the individual events by setting the notifyFlag parameter to false. Once the loop has finished, UpdateThread fires a single "everything changed" event.

Delaying Publishing Changes

To keep the application responsive, changes are not published when the user is scrolling. You can check for scrolling by attaching a ChangeListener to the horizontal and vertical JScrollBars contained in the JScrollPane. For example:

  ChangeListener changeListener = new ChangeListener() {
    public void stateChanged(ChangeEvent e) {
      BoundedRangeModel m = (BoundedRangeModel)(e.getSource());
      setUpdatesEnabled(!(m.getValueIsAdjusting()));
    }
  };
  sp.getVerticalScrollBar().getModel().addChangeListener(changeListener);
  sp.getHorizontalScrollBar().getModel().addChangeListener(changeListener);

In the preceding code, setUpdatesEnabled(false) is invoked when the user starts scrolling. When the scrolling stops, setUpdatesEnabled(true) is invoked.

Attaching the ChangeListener allows determining when the user is scrolling, but there are often other times where the application may be busy responding to user requests and updates should be avoided. A convenient, simple way to check for a busy application is to check whether any pending events are on the EventQueue:

    EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
    while (queue.peekEvent() != null) {
        sleep(10);
    }

This code loops until no events are on the EventQueue. It isn't foolproof, but it's a good start that doesn't require additional changes to the rest of the application.

Publishing changes now looks like this:

    public void publishChanges() {
        // Wait until the user isn't scrolling
        synchronized(this) { 
            while (!updatesEnabled) {
                wait();
            }
        }
        // And wait until there are no pending events.
        EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
        while (queue.peekEvent() != null) {
            sleep(10);
        }
        Runnable publishRunnable = new Runnable() {
            public void run() {
                publishChangesOnEventDispatchingThread();
            }
        };
        // publish the changes on the event-dispatching thread
        SwingUtilities.invokeAndWait(publishRunnable);
        // Wait until the system has completed processing of any
        // events we triggered as part of publishing changes.
        SwingUtilities.invokeAndWait(emptyRunnable);
    }

An important thing to note is that although we delay publishing of changes, the delay only occurs when the user is busy with the application and not likely to notice that we have delayed notification. The GeneratorThread continues to generate changes, and as soon as the system is ready we publish them to the user. Nothing is lost.

Results

The following table shows the time to paint the visible contents of the table with various optimizations. The optimizations have been cumulatively applied, so that the last line is with all the optimizations outlined in this article. The timings come from an Ultra 60 workstation and version 1.4.

Optimizations Painting Time (in ms)
Baseline 140
+ override getTableCellRendererComponent   134
+ override firePropertyChange 131
+ override TableCellRenderer.paint 128
+ create custom CellRendererPane 104
+ miscellaneous CellRenderer overrides 99

The following table shows the before and after times of publishing a set of 15,000 changes to the model.

Optimization Publishing Time (in ms)
Baseline 331
+ VisibleTableModelEvent   48

Warning

This article advocates short circuiting the cloning of the Graphics passed to the Renderer. While this provides a dramatic performance boost it has the potential to cause rendering artifacts in future versions of Swing that offer different painting behavior. This is very unlikely to happen, but developers wishing to support multiple JREs should be aware of this.



Conclusion

As we've just shown, a handful of targeted tweaks can improve painting performance by roughly 30% and notification by a factor of 7. These improvements are possible because we understand the nature of the application and how it uses JTable, and we can short-circuit unused features.

Can we do more? You bet! A future article will discuss creating a custom RepaintManager, as well as how to deal with JTable if you cannot batch updates.

Left Curve
Java SDKs and Tools
Right Curve
Left Curve
Java Resources
Right Curve
JavaOne Banner
Java 8 banner (182)