TonyRomero TonyRomero - 1 month ago 20
Java Question

Synchronized objects removes from a reverse list [JList - SWING]

I'm having troubles managing an ArrayList using a custom AbstractListModel.
Here's is my model:

TradesListModel.class

package window;

import java.util.ArrayList;

import javax.swing.AbstractListModel;

import main.Trade;

public class TradesListModel extends AbstractListModel<Object>{

/**
*
*/
private static final long serialVersionUID = 1L;
private ArrayList<Trade> trades;

public TradesListModel(){
trades = new ArrayList<Trade>();
}

public void add(Trade trade){
trades.add(0, trade);
this.fireIntervalAdded(this, 0, 1);
}

public void remove(){
trades.remove(trades.size()-1);
this.fireIntervalRemoved(this, trades.size(), trades.size());
}

public void clear(){
trades.clear();
this.fireIntervalRemoved(this, 0, trades.size());
}

@Override
public int getSize() {
return trades.size();
}

@Override
public Object getElementAt(int index) {
return trades.get(index);
}

}


As you can see every object is added to the top of my list by three threads working in concurrency. Every thread adds new objects by calling a method in my main class:

public void addTrade(Trade trade){
trades.add(trade);
SwingUtilities.invokeLater(new Runnable(){

@Override
public void run() {
myModel.add(trade);
}

});
}


I have another
newSingleThreadScheduledExecutor
that every 30 seconds call the method of my main class
remove()
to remove objects from the list handled by my custom model:

public boolean updateTrades(){
Iterator<Trade> it = trades.iterator();
try {
synchronized(this){
index = 0;
while(it.hasNext()){
Trade current = it.next();
if(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()-current.getTime())>TIME){
it.remove();
SwingUtilities.invokeLater(new Runnable(){

@Override
public void run() {
lm.remove(index);
}
});
}
index++;
}
}
return true;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}


The
trades
list is equal to the
trades
list into the model.
What I've done is print the index value passed to the model but it doesn't correspond to the index into the main class as I get this exception multiple times:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -6
at java.util.ArrayList.elementData(ArrayList.java:418)
at java.util.ArrayList.remove(ArrayList.java:495)
at window.TradesListModel.remove(TradesListModel.java:28)
at main.TradeFinder$2.run(TradeFinder.java:119)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)


TradeFinder
is my main class.

Answer

Let's consider the following example: updateTrades was called when there are three trades in the list: [t0, t1, t2] and all of them should be removed.

In this case you will call lm.remove method three times: with indices 0, 1, and 2. After the first the list will contain [t1, t2], and after the second: [t2]. When you call it third time, there is no element with index 2, so you'll get IndexOutOfBoundsException.

To fix it, just call lm.remove(0) three times. This will remove first three elements, as expected.