Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / GUI / December 2005

Tip: Looking for answers? Try searching our database.

efficiency of JList setElementAt()

Thread view: 
Raymond Cruz - 19 Dec 2005 22:49 GMT
I have an application that displays about 130 text lines in a scrollable
JList via the DefaultListModel.  Approximately 1 entry is modified each
second which is done by determining the position of the item and invoking
setElementAt of the DefaultListModel object.  My Athlon XP-1800+ machine
consumes 31% of the system CPU when these operations occur but if I execute
all the program logic with the single exception of the setElementAt call, it
consumes only 3% of the CPU.  Is it reasonable that modifying one element of
a list once a second would consume so much CPU?  Also, the modification time
increases approximately linearly with list size.  If I make the list 1/2 the
size about 19% of the CPU is consumed.

It seems that if my list size grows, frequency of updates increases, or if I
operate more than a single list at a time (all of which are planned), I will
drag even a quite fast machine to its knees.  Are there more efficient
alternatives available?  The target machine is Windows, JRE 1.4.2_08 and the
development environment is Eclipse 3.1.

RC
Thomas Hawtin - 19 Dec 2005 23:23 GMT
> I have an application that displays about 130 text lines in a scrollable
> JList via the DefaultListModel.  Approximately 1 entry is modified each
[quoted text clipped - 12 lines]
> alternatives available?  The target machine is Windows, JRE 1.4.2_08 and the
> development environment is Eclipse 3.1.

PL&F revalidates and repaints the entire list if any of it changes.
Fortunately, there should only be so much on the screen that can be
revalidate and repainted. However, the quoted times do seem excessive.

Tom Hawtin
Signature

Unemployed English Java programmer
http://jroller.com/page/tackline/

Raymond Cruz - 19 Dec 2005 23:27 GMT
> > I have an application that displays about 130 text lines in a scrollable
> > JList via the DefaultListModel.  Approximately 1 entry is modified each
[quoted text clipped - 18 lines]
>
> Tom Hawtin

The problem isn't very sensitive to how much is on the screen.  For the
examples given, approximately 75% of the list was outside of the scroller's
viewport.  In fact, if the frame that displays the JList is minimized to a
null display, the timing improves by only about 15%.

RC
Thomas Hawtin - 20 Dec 2005 00:01 GMT
> The problem isn't very sensitive to how much is on the screen.  For the
> examples given, approximately 75% of the list was outside of the scroller's
> viewport.  In fact, if the frame that displays the JList is minimized to a
> null display, the timing improves by only about 15%.

You probably need to use a profiler on it (or do a ctrl-\ or ctrl-break
on it randomly from the console). Possibly it is something to do with
validating the layout, the model/list elements might be slow or perhaps
you are extremely short on video RAM.

I wrote a little benchmark. On 266 MHz PII it settled down to 3-3.5%
CPU. On my 2.26ish GHz Celeron, it came in comfortably under 1%.

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

class ListSpeed {
    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {
                public void run() {
                    swing();
                }
        });
    }
    private static final java.util.Random random =
        new java.util.Random();
    private static void swing() {
        assert java.awt.EventQueue.isDispatchThread();
        JFrame frame = new JFrame("ListSpeed");
        final DefaultListModel model = new DefaultListModel();
        for (int ct=0; ct<50; ++ct) {
            model.addElement("some data "+new java.util.Date());
        }
        final JList list = new JList(model);
        frame.add(list);//new JScrollPane(list));//
        frame.pack();
        frame.setVisible(true);
        new javax.swing.Timer(1000 /* millis */, new ActionListener() {
                private boolean set;
                public void actionPerformed(ActionEvent event) {
                    model.setElementAt(
                        "new data "+new java.util.Date(),
                        random.nextInt(model.size())
                    );
                }
       }).start();
   }
}

Tom Hawtin
Signature

Unemployed English Java programmer
http://jroller.com/page/tackline/

Raymond Cruz - 20 Dec 2005 00:58 GMT
Thanks Tom.  I agree with your results when I run your program so that made
me immediately consider something that I hadn't considered important before.
My list objects are HTML strings solely for the purpose of highlighting a
few words in a different color.  This appears to make the list update about
an order of magnitude more costly!  If you modify your program to produce
strings about 3 times as long, make the strings HTML with a font color tag,
and increase the list size to about 130, I think you'll get the kind of
results I cited in my first post.  (Note: by virtue of the fact that your
program did not compile with my 1.4.2 JDK, I suspect you are using an
earlier Java version and I know that HTML support has been introduced over
time so I'm not sure if you'll be able to use HTML strings).

So I have a new question -- is there a way to produce different font colors
that is much more efficient than using HTML strings in Java?

Thanks again for your persistence.

RC

> > The problem isn't very sensitive to how much is on the screen.  For the
> > examples given, approximately 75% of the list was outside of the scroller's
[quoted text clipped - 47 lines]
>
> Tom Hawtin
Thomas Hawtin - 20 Dec 2005 01:26 GMT
> results I cited in my first post.  (Note: by virtue of the fact that your
> program did not compile with my 1.4.2 JDK, I suspect you are using an
> earlier Java version and I know that HTML support has been introduced over
> time so I'm not sure if you'll be able to use HTML strings).

Later version. It was only the assert that trips up 1.4 with default
options. If you add -source 1.4 (which should have been the default if
Sun were awake), then it will compile.

> So I have a new question -- is there a way to produce different font colors
> that is much more efficient than using HTML strings in Java?

A ListCellRenderer is what you want. It's pretty easy to modify the
DefaultListCellRenderer to change colour of the whole text. To just do
part of the text, you will need something a little more sophisticated.
Instead of Strings in the ListModel, use a custom class describing the
required display. The ListCellRenderer can then cast the cell data back
and paint something appropriate. I suggest keeping the renderer as
simple as possible, pushing as much logic back to the cell data model as
possible.

Tom Hawtin
Signature

Unemployed English Java programmer
http://jroller.com/page/tackline/

Roedy Green - 20 Dec 2005 05:08 GMT
>So I have a new question -- is there a way to produce different font colors
>that is much more efficient than using HTML strings in Java?

For JTree you write customCellRenderers. That should be many times
faster than using HTML.  Presumably there is a similar feature for
your component.

The key is a method something like this:

 /**
   *  Returns the component used for drawing the cell.  This method
is
   *  used to configure the renderer appropriately before drawing.
   *  Associated TableModel must implement DangerTableModel.
   *
   * @param    table           the <code>JTable</code> that is asking
the
   *                           renderer to draw; can be
<code>null</code>
   * @param    value           the value of the cell to be rendered.
It is
   *                           up to the specific renderer to
interpret
   *                           and draw the value.  For example, if
   *                           <code>value</code>
   *                           is the string "true", it could be
rendered as a
   *                           string or it could be rendered as a
check
   *                           box that is checked. <code>null</code>
is a
   *                           valid value
   * @param    isSelected      true if the cell is to be rendered
with the
   *                           selection highlighted; otherwise false
   * @param    hasFocus        if true, render cell appropriately.
For
   *                           example, put a special border on the
cell, if
   *                           the cell can be edited, render in the
color used
   *                           to indicate editing
   * @param    row             the row index of the cell being drawn.
When
   *                           drawing the header, the value of
   *                           <code>row</code> is -1
   * @param    column          the column index of the cell being
drawn
   */
  public Component getTableCellRendererComponent( JTable table,
                                                  Object value,
                                                  boolean isSelected,
                                                  boolean hasFocus,
                                                  int row,
                                                  int column)
     {

     Component cell = super.getTableCellRendererComponent( table,
                                                           value,
isSelected,
                                                           hasFocus,
                                                           row,
                                                           column);

     int dangerLevel =
((DangerTableModel)(table.getModel())).getDangerLevel( row );
   

     // normal
     cell.setForeground( winAttrScheme.getForeground( dangerLevel )
);
     cell.setBackground( winAttrScheme.getBackground( dangerLevel )
);

     cell.setFont( winAttrScheme.getFont ( dangerLevel ) );
     return cell;
     }

Signature

Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.

Thomas Hawtin - 22 Dec 2005 01:50 GMT
> Thanks Tom.  I agree with your results when I run your program so that made
> me immediately consider something that I hadn't considered important before.
[quoted text clipped - 10 lines]
> So I have a new question -- is there a way to produce different font colors
> that is much more efficient than using HTML strings in Java?

It occurred to me that you can keep your HTML and make it go at a
reasonable speed (unless you run low on memory). The Swing cell renderer
design is based on assumptions that construction is expensive and
updating values is cheap. Clearly, even for the humble JLabel, the
second is not correct when the value is HTML. So let's cache.

The approach I have taken is to have a renderer lazily create a
conventional renderer for each index. The renderers are softly
referenced, in a naive manner. There are lots of variations with
different trade-offs. This seems to work quite well for me. It is worth
noting that the classical approach is probably going to be faster to
show the initial screen. And my class name is too cute for production use.

The only necessary addition to original program is:

        list.setCellRenderer(new DaisyListCellRenderer());

I think I shall add an improved version to my weblog.

Tom Hawtin

(Usual disclaimer.)

import java.lang.ref.Reference;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.swing.*;

class DaisyListCellRenderer implements ListCellRenderer {
    private final Map<
        JList,List<Reference<ListCellRenderer>>
    > caches =
        new java.util.WeakHashMap<
            JList,List<Reference<ListCellRenderer>>
        >();
    public java.awt.Component getListCellRendererComponent(
        javax.swing.JList list,
        Object value,
        int index,
        boolean isSelected,
        boolean cellHasFocus
    ) {
        // Find cache for this list.
        List<Reference<ListCellRenderer>> cache = caches.get(list);
        if (cache == null) {
            cache =
                new java.util.ArrayList<Reference<ListCellRenderer>>();
            caches.put(list, cache);
        }

        // Cache list may need extending.
        int shortfall = index+1 - cache.size();
        if (shortfall > 0) {
            assert cache.size()+shortfall == index+1;
            cache.addAll(
                Collections.<Reference<ListCellRenderer>>nCopies(
                    shortfall, null
                )
            );
        }

        // Find renderer.
        Reference<ListCellRenderer> rendererRef = cache.get(index);
        ListCellRenderer renderer =
            rendererRef==null ? null : rendererRef.get();
        if (renderer == null) {
            renderer = createRenderer(/*...*/);
            cache.set(
                index,
                new java.lang.ref.SoftReference<ListCellRenderer>(
                    renderer
                )
            );
        }

        // Forward.
        return renderer.getListCellRendererComponent(
            list, value, index, isSelected, cellHasFocus
        );
    }
    private ListCellRenderer createRenderer(/*...*/) {
        return new javax.swing.DefaultListCellRenderer();
    }
}
class LighstSpeed {
    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {
                public void run() {
                    swing();
                }
        });
    }
    private static final java.util.Random random =
        new java.util.Random();
    private static void swing() {
        assert java.awt.EventQueue.isDispatchThread();
        JFrame frame = new JFrame("LighstSpeed");
        final DefaultListModel model = new DefaultListModel();
        for (int ct=0; ct<50; ++ct) {
            model.addElement("<html>some data "+new java.util.Date());
        }
        final JList list = new JList(model);
        list.setCellRenderer(new DaisyListCellRenderer());
        frame.add(list);//new JScrollPane(list));//
        frame.pack();
        frame.setVisible(true);
        new javax.swing.Timer(1000, new ActionListener() {
                private boolean set;
                public void actionPerformed(ActionEvent event) {
                    model.setElementAt(
                        "<html>new data "+new java.util.Date(),
                        random.nextInt(model.size())
                    );
                }
        }).start();
    }
}
Signature

Unemployed English Java programmer
http://jroller.com/page/tackline/

Raymond Cruz - 22 Dec 2005 17:11 GMT
I wrote a cell renderer tailored to my application and it has resulted in a
performance improvement by at least a factor of 5.  I include it below in
case it might be useful to anyone and for a critique if anyone is so
motivated since I don't consider myself to be much more than an advanced
beginner in Java.

However I still have performance questions and hopes for better performance.
The construction of my list is incremental since it builds as messages
arrive.  However after some number of minutes it reaches steady state in
size and order and afterwards single cells are modified in place with new
data.  My hope and expectation is that only a single cell would be
invalidated and re-rendered in this operation but it appears that either the
entire list is invalidated or all cells from the changed cell to the end of
the list are invalidated.  I base this conclusion on the fact that the
performance penalty for my updates increases fairly linearly with list size.

Is it possible to achieve constant update time for such a list?  What would
one need to do to achieve that goal?

RC

/*
* This class is a cell renderer that supports the capability of
* highlighting substrings in a JList of strings with a different
* color.  This capability and more can be achieved by using HTML
* with a much higher performance penalty.  Initially table entry
* color is black but it can be changed within the value string by
* a substring consisting of "\u00A7RRGGBB" where '\u00A7 is the
* unicode for a "section marker" and RRGGBB are the hexadecimal
* values for the red, green, and blue color components.
*/

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.FontMetrics;
import javax.swing.JPanel;
import javax.swing.JList;
import javax.swing.ListCellRenderer;

public class ColorCellRenderer extends JPanel implements ListCellRenderer {

 private static final long serialVersionUID = 1L;
 private static final String DELIMITER = "\u00A7";
 private JList list;
 private String cv;
 private int fontSize;
 private boolean selected;
 private int maxWidth = 0;

 // default constructor, 12 point font
 public ColorCellRenderer() {
   this.fontSize = 12;
 }

 // this constructor allows font point size to be specified
 public ColorCellRenderer(int fontSize) {
   this.fontSize = fontSize;
 }

 // implement the abstract method of the interface
 public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
   this.list = list;
   selected = isSelected;
   cv = value.toString();
   return this;
 }

 // override of paint() to render the cell.  interpret substrings
 // that specify font color changes and paint the background of the
 // selected cell.
 public void paint(java.awt.Graphics g) {
   int text_x = 0;
   int text_y;
   boolean colorChange = false;

   g.setFont(new Font("Arial", Font.PLAIN, fontSize));
     FontMetrics fm = g.getFontMetrics();
     int fontHeight = fm.getHeight();

     if(selected) {
       g.setColor(list.getSelectionBackground());
       g.fillRect(0, 0, this.getWidth(), this.getHeight());
     }
     g.setColor(Color.black);
     text_y = fontHeight-2;
     java.util.StringTokenizer st = new java.util.StringTokenizer(cv,
DELIMITER, true);
     while(st.hasMoreTokens()) {
       String s = st.nextToken();
       if(colorChange) {
         colorChange = false;
         if(s.length() >= 6) {
           int newColor;
           String colorString = s.substring(0, 6);
           try {
             newColor = Integer.parseInt(colorString, 16);
           } catch(Exception e) {newColor = 0;}
           g.setColor(new Color(newColor));
           s = s.substring(6);
         }
       }
       if(s.equals(DELIMITER)) {
         colorChange = true;
       } else {
         g.drawString(s, text_x, text_y);
         text_x += fm.stringWidth(s);
       }
     }
     if(text_x > maxWidth)
       maxWidth = text_x;
     setPreferredSize(new java.awt.Dimension(maxWidth, fontHeight+1));
     g.dispose();
 }
}

> >  <snip>
> > So I have a new question -- is there a way to produce different font colors
[quoted text clipped - 8 lines]
> The approach I have taken is to have a renderer lazily create a
> conventional renderer for each index.  <snip>
Thomas Hawtin - 22 Dec 2005 18:42 GMT
> However I still have performance questions and hopes for better performance.
> The construction of my list is incremental since it builds as messages
[quoted text clipped - 5 lines]
> the list are invalidated.  I base this conclusion on the fact that the
> performance penalty for my updates increases fairly linearly with list size.

Telling the graphics card to go off and paint some strings shouldn't be
the taxing part of the operation.

> Is it possible to achieve constant update time for such a list?  What would
> one need to do to achieve that goal?

The Basic PL&F (javax.swing.plaf.basic.BasicListUI) always repaints the
entire control. I guess you could arrange for it to be double buffered,
but I doubt that would be a sensible use of effort.

> public class ColorCellRenderer extends JPanel implements ListCellRenderer {

You might as well extend JComponent (the idea that JPanel is
automatically opaque is untrue in general).

> [...]
>   public void paint(java.awt.Graphics g) {

Probably should be paintComponent, I think.

>     int text_x = 0;
>     int text_y;
>     boolean colorChange = false;

It is generally better to move declaration down to where they are used.

>     g.setFont(new Font("Arial", Font.PLAIN, fontSize));

Creating a Font can be expensive, depending on platforms. The two
obvious options are caching and put the Font within the value object.

Renderers work out better if they are as thin as possible, with the real
meat down in the model side of things. So if the values from the model
already parsed the string and had Fonts and Colors in place, it'd
probably be faster, more understandable, more testable, etc.

> [...]
>             try {
>               newColor = Integer.parseInt(colorString, 16);
>             } catch(Exception e) {newColor = 0;}

You can be a bit more specific about which exception you want to catch.

>             g.setColor(new Color(newColor));
>             s = s.substring(6);
[quoted text clipped - 10 lines]
>         maxWidth = text_x;
>       setPreferredSize(new java.awt.Dimension(maxWidth, fontHeight+1));

It's not a good idea to mutate a component within paint. This should be
done in getListCellRendererComponent. You might get away with it
painting over again, but it isn't fast or pretty.

>       g.dispose();

It wasn't your Graphics object, so don't dispose it.

>   }
> }

Tom Hawtin
Signature

Unemployed English Java programmer
http://jroller.com/page/tackline/

Roedy Green - 20 Dec 2005 05:03 GMT
>I have an application that displays about 130 text lines in a scrollable
>JList via the DefaultListModel.  Approximately 1 entry is modified each
[quoted text clipped - 4 lines]
>consumes only 3% of the CPU.  Is it reasonable that modifying one element of
>a list once a second would consume so much CPU?

look at the code for DefaultListModel.setElementAt
 public void setElementAt(Object obj, int index) {
       delegate.setElementAt(obj, index);
       fireContentsChanged(this, index, index);

delegate is an ordinary Vector. The problem coming from the busywork
inspired by fireContentsChanged.
Just what are you doing to render that row?
Signature

Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.



Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.