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:
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?
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.