2

I am writing a GUI in swing to display incomming requests from clients. The server populates a LinkedBlockingQueue, while another thread takes from the queue when there is data available. The requests are stored as an Object. Like so:

while(should_take) {
    //getRequest() is blocking
    Object request = server.getRequest();

    //Add request to user interface
    GUI.addToList(request);
}

Now my question comes in, would it be better to:

Solution 1:

Store the request in a ConcurrentHashMap<Integer, Object> with the key as the hash of the request and the value as the Object. I will then use a DefaultListModel to store the identifier (eg. request type) of the request and the hash value. The DefaultListModel will be used to populate a JList, effectively displaying the request to the user. The value of the selected request (selected by the user) can then be retrieved from the ConcurrentHashMap using the hash saved in the DefaultListModel.

Some example code:

ConcurrentHashMap<Integer, Object> requests = new ConcurrentHashMap<>();
DefaultListModel listData = new DefaultListModel();
JList theList = new JList();
...
public void addToList(Object request) {
    //Place in HashMap
    requests.put(request.hashCode(), request);

    //Create a DataHolder with the Hashvalue and identifier of the request
    DataHolder holder = new DataHolder(request.getID(), request.hash);

    //Add the element to the ListModel
    listData.addElement(holder);

    //Assign the list model to the JList
    theList.setModel(listData);
}

When a user selects an item in the list:

DataHolder holder = (DataHolder)theList.getSelectedValue();
//Get request from HashMap

Object request = requests.get(holder.getHash());
//Do something with request

Solution 2:

I populate a new Object, call it DataHolder, with the request identifier and request value. I can now populate the JList with DefaultListModel that contains DataHolder and no reference is needed to any other data structure to retrieve the actual request value. Because DefaultListModel is used to populate the JList, I feel it can impact performance and may cause the list to populate/unpopulate slower.

Some example code:

DefaultListModel listData = new DefaultListModel();
JList theList = new JList();
...
public void addToList(Object request) {
    //Removed HashMap

    //Create a DataHolder with the Hashvalue and *actual value* of the request
    DataHolder holder = new DataHolder(request.getID(), request);

    //Add the element to the ListModel
    listData.addElement(holder);

    //Assign the list model to the JList
    theList.setModel(listData);
}

When a user selects an item in the list:

//No more HashMap
DataHolder holder = (DataHolder)theList.getSelectedValue();

Object request = holder.getData();
//Do something with request

Which solution will yield quicker results? Is there a more efficient way to do this? Any help on the matter will be greatly appreciated.

Some more info:

  • Request may be delivered in bursts. (50+ per burst)
  • Requests will consist of between 20 and 50 lines of XML
  • Requests will be removed from the data structure randomly

EDIT:

The sequence that adds messages to the list has now been wrapped in invokeLater. With my implementation, every time a message will be added to the list, a new thread will be created to do all the work, and end once the message is in the list. Surely this effects the answer. If 50 threads are created in tight succession (with each call to addToList), which solution will execute quicker?

Ian2thedv
  • 2,691
  • 2
  • 26
  • 47
  • 2
    simpler would be to play with DefaultListModel, espacially in the case that there are changes ...., note you have an issue with Concurency in Swing, output from GUI.addToList(request); to Swing method must be wrapped into invokeLater – mKorbel Mar 25 '14 at 09:06
  • @mKorbel thanks for solving problems I didn't even know about!! My GUI went out of sync sometimes, so I throttled the rate of incoming requests. This fixed it permanently. Thank you so much – Ian2thedv Mar 25 '14 at 10:04
  • (your edit) DefaultListModel is fully MVC whatever, no idea how is/are thread(s) invoked (practically doesn't matter) but all signals should be ending in one place, in the model (DefaultListModel), there no issue to add/remove/insert, anything (from 50 threads concurently) at runtime, problem will be in the case that JLists view is activelly used by user, then you have to use AbstractListModel (can be based on any array, note <> HashXxx) by override ListSelectionEvents if you want to play with selection in JList view – mKorbel Mar 25 '14 at 10:22

1 Answers1

2

Solution 3: Extend SwingWorker,

class MessageWorker extends SwingWorker<List<DataHolder>, DataHolder> {}

In your implementation of doInBackground(), publish() intermediate results as they become available. In your implementation of process(), update the view component's model. Conveniently, SwingWorker will coalesce publish() invocations at a sustainable pace. Profile your application to verify. More examples may be found here.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Correct me if I'm wrong, but isn't StringWorker essentially just making use of `Thread` and `Runnable` and allowing clean access to all the wonderful threading functionality? I did implement this, and the list can now be accessed whilst being updated, which is great. It seems you favor solution 2 then to some extent? I assume you mean I should populate the `DefaultListModel` in `MessageWorker`, and upon reaccessing removing it again? – Ian2thedv Mar 25 '14 at 14:36
  • While other approaches can be made to work, `SwingWorker` offers _much_ more via `Runnable`, `Future`, `RunnableFuture` & `PropertyChangeEvent`; you should update the `ListModel` _only_ on the EDT, e.g. in `process()`; in addition to speed, try to favor *liveness* by letting the worker pace the view updates; users tolerate waiting much better when there's _something_ to see. – trashgod Mar 25 '14 at 18:21
  • 1
    Thank you, I implemented everything, and solution 3 takes the gold – Ian2thedv Mar 26 '14 at 13:16