简体   繁体   English

ArrayList索引超出范围,尽管访问的索引在范围之内

[英]ArrayList index out of bounds though the accessed index is within bounds

I am getting an Index Out of Bounds exception for an ArrayList . 我正在获取ArrayList的“索引越界”异常。 At the point of exception the size() and index seem to be fine when printed. 在例外情况下,打印时size()和index似乎很好。 Below is the relevant code. 以下是相关代码。 I have a custom table model which is refreshed when a button is clicked. 我有一个自定义表模型,单击按钮后会刷新该模型。

public class CallingClass
{
    public void buttonClicked()
    {
        new Thread(new Runnable()
        {
            public void run()
            {                   
                dataTableModel.refresh();
            }
        }).start();     
    }
}

public class DataTableModel extends AbstractTableModel
{
    protected ArrayList<Object> data;   

    public DataTableModel()
    {
        data = new ArrayList<Object>();
    }

    public Object getValueAt(int modelRow, int modelColumn)
    {
         // Throws Index Out of Bounds Exception though a println here shows modelRow value within 0 to data.size()-1
         // DataObject is just an interface to support getValue method
        return ((DataObject) data.get(modelRow)).getValue();
    }

    public void refresh()
    {
        reloadData();
        fireTableDataChanged();
    }

    protected void reloadData()
    {
        ArrayList<TextMessage> messageList = jmsConnection.getMessageList();

        data.clear();

        try
        {
            for(int i=0; i<messageList.size(); ++i)
            {
                data.add(new MyDataObject(messageList.get(i).getJMSMessageID()));
            }
        }
        catch(Exception e)
        {
        }
    }
}

As you can see the DataTableModel.refresh() runs in a new thread. 如您所见, DataTableModel.refresh()在新线程中运行。 The call to fireTableDataChanged probably causes the eventQueue thread to redraw the table, which in turn calls the getValueAt method. 调用fireTableDataChanged可能会导致eventQueue线程重新绘制表,从而依次调用getValueAt方法。 Here the exception is thrown, despite the modelRow value being less than data.size() . 尽管modelRow值小于data.size() ,但modelRow引发异常。 Not sure how there can be race condition either because the Runnable thread calls the fireTableDataChanged after calling reloadData (which repopulates the data object). 也不知道怎么可能存在竞争状况,因为Runnable线程在调用reloadData (重新填充数据对象)之后调用fireTableDataChanged Hence the data object should be stable when the eventQueue thread is calling getValueAt . 因此,当eventQueue线程正在调用getValueAt时, data对象应该是稳定的。 Also note the the refresh button is disabled until the runnable thread comes out, to avoid overlapping calls to refresh. 还要注意,刷新按钮将被禁用,直到可运行线程出来为止,以避免重叠的刷新调用。

Below is the exception message 以下是异常消息

Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Index: 3, Size: 10
    at java.util.ArrayList.rangeCheck(Unknown Source)
    at java.util.ArrayList.get(Unknown Source)
    at datatablemodels.DataTableModel.getValueAt(DataTableModel.java:70)

If I change the reloadData code slightly, the exception goes away (at least not reproducible yet). 如果我稍稍更改reloadData代码,则异常消失(至少现在还不能重现)。 My guess is that it is just reducing the odds of the exception. 我的猜测是,这只是在减少例外的几率。

protected void reloadData()
{
    ArrayList<TextMessage> messageList = jmsConnection.getMessageList();

    ArrayList<Object> tempdata = new ArrayList<Object>();

    try
    {
        for(int i=0; i<messageList.size(); ++i)
        {
            tempdata.add(new MyDataObject(messageList.get(i).getJMSMessageID()));
        }
    }
    catch(Exception e)
    {
    }

    data = tempdata ;
}

I am more interested in understanding what is going on here. 我对了解这里发生的事情更感兴趣。 I do have alternate solutions like synchronized , CopyOnWriteArrayList etc. 我有类似替代解决方案synchronizedCopyOnWriteArrayList等。

Since you have no synchronization, you have no guarantee about when data changes made in the refresh() method - which runs in its own thread - will be visible in other threads running the getValueAt() method, if ever. 由于没有同步,因此无法保证在运行自己的线程的refresh()方法中进行的数据更改何时会在其他运行getValueAt()方法的线程中可见。 For example, the getValueAt() method, or part of it, may run "between" the data.clear() call in the reloadData() method and the repopulation of the data table. 例如,getValueAt()方法或其中的一部分可以在reloadData()方法中的data.clear()调用与数据表的重新填充之间“运行”。

And when you put the println() calls in, they may run before the data.clear() call or after the repopulation of data, even if the data.get() call does not. 并且当您放置println()调用时,它们可能在data.clear()调用之前或在重新填充数据之后运行,即使data.get()调用没有。

The bottom line is, whenever data is touched by more than one thread, as it is here, you must synchronize access to the data, one way or another. 最重要的是,每当一个以上的线程触摸数据时,都必须以一种或另一种方式同步访问数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM