0

Preliminary question

How can I build a JTable so that it would be updated when a Google Guava Event is fired (And guarantee it's thread safe)?

The simple way would be to do model.setValueAt(aValue, row, column);

Two problems:

  • using the standard data structure of JTableModel, I don't know what row I'm in.
  • using the standard data structure of JTableModel, I can't use this data for other components
  • this is not thread safe (I think)

I have thought of changing JTableModel to use ConcurrentHashMap but it seems as this is not the intended purpose of extending it. I feel this is a route to strange bugs.

I feel very lost in the middle of all this, if you can just supply a guide of what I should explore first it would help a lot.

A more informed question

The events are not in EVT. To make sure I ran SwingUtilities.isEventDispatchThread(), the result was false.

What I now think I should do:

@Subscribe
public void handleMessage(DataStructure newInformation) {
  // handle the information

  SwingUtilities.invokeLater(new Runnable() {
    @Override
    public void run() {
        tableModel.setValueAt(...); // update table1
        tableModel2.setValueAt(...); // update table2
    }
  });
}

In the event I get information that is totally independent of the table. I need a way to know to what row the information relates to.
I can always cycle through the whole vector in the models but I am looking for a solution where I can use a map or array.

kotoko
  • 599
  • 2
  • 6
  • 22

2 Answers2

3

Every change to a Swing component must be done in the event dispatch thread. You can test if you're in the event dispatch thread by calling SwingUtilities.isEventDispatchThread(). If it returns true, you can modify your model directly. If it returns false, then the update must be done in the EDT by wrapping it inside a Runnable executed with SwingUtilities.invokeLater() (or SwingUtilities.invokeAndWait()):

SwingUtilities.invokeLater(new Runnable() {
    @Override
    public void run() {
        tableModel.setValueAt(...);
    }
});

You didn't say what this event was, and what is your table model, but you of course need to be able, thanks to the table model, to find which row(s) and column(s) correspond to the event you get. An AbstractTableModel is typically used, wrapping a List<SomeBean>. Using the methods of the list, you should be able to find the index of the SomeBean instance to update.

EDIT:

As I said, the typical way of defining a table model is to extend AbstractTableModel, and implement the methods based on a wrapped List. But nothing prevents you to use a Map instead or in addition to a List, and to add additional methods to the model, provided the table model contract is implemented. The List would be used to implement the methods getValueAt(), getRowCount(), etc., and the map would allow to quickly find the index of an object in the list, for example. Of course, it's your job to encapsulate everything into a custom table model, and to keep both collections in sync.

That said, I would go with a simple solution of iterating on the list to find the appropriate row, and only try to optimize if you really have a performance problem (which would probably only occur for a huge table). Maintaining the two collections could have a bigger overhead than simply looping on the list. Don't optimize prematurely: it's the root of all evil.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
1

My general advice would be to use an AsyncEventBus with an Executor that implements execute(Runnable) by calling SwingUtilities.invokeLater(Runnable). That will cause all event handlers for that event bus to only be called on the EDT.

If you have lots of events that you don't want handled on the EDT, you could use two event buses, with only one calling event handlers on the EDT (the UI event bus). Then your UI components subscribe to the UI event bus. You can then have your main event bus forward certain types of events to the UI using an intermediate object that's registered with the main event bus that just posts events it receives to the UI event bus.

ColinD
  • 108,630
  • 30
  • 201
  • 202