NullPointerException в пользовательской модели таблицы

Я написал пользовательскую модель таблицы для JTable:

class MessageTableModel{

    private static Set<Message> messages = Collections.synchronizedSet(new TreeSet<Message>());

    .
    .
    .

    public void setMessages(List<Message> newMessages) {
        Collections.sort(newMessages);
        Iterator<Message> it = messages.iterator();
        while (it.hasNext()) {
            Message mess = it.next();
            if (!newMessages.contains(mess)) {
                it.remove();
                this.fireTableDataChanged();
            }
        }
        for (Message message : newMessages)
            if (message.isOrderStatusMessage())
                if (!messages.contains(message)) {
                    addMessage(message);
                }
        this.fireTableDataChanged();
    }

    public Message getMessageAtRow(int row){
        return (Message) messages.toArray()[row];
    }
}

Проблема в том, что есть поток, который обновляет значения таблицы, периодически вызывая метод setMessages(). Если я попытаюсь получить строку во время этого обновления:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1

в этой строке:

return (Message) messages.toArray()[row];

Есть способ заставить метод getMessageAtRow() ожидать выполнения изменений или другое решение этой проблемы?


person elias    schedule 16.04.2012    source источник


Ответы (1)


Swing однопоточный. Вы не можете изменить модель в потоке вне потока событий. Самый простой способ исправить это:

public void setMessages(List<Message> newMessages) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
            Collections.sort(newMessages);
        Iterator<Message> it = messages.iterator();
        while (it.hasNext()) {
            Message mess = it.next();
            if (!newMessages.contains(mess)) {
                it.remove();
                this.fireTableDataChanged();
            }
        }
        for (Message message : newMessages)
            if (message.isOrderStatusMessage())
                if (!messages.contains(message)) {
                    addMessage(message);
                }
        this.fireTableDataChanged();
    }
  )};
}

Кроме того, fireTableDateChanged() также следует вызывать только в потоке событий.

person Reverend Gonzo    schedule 16.04.2012
comment
Дополнительные сведения см. также в разделе Параллелизм в Swing. - person Andrew Thompson; 16.04.2012
comment
я понял, что, действительно, проблема в том, что метод table.getSelectedRow() иногда возвращает -1, но я не знаю, почему... - person elias; 17.04.2012
comment
@Elias, ты должен проверить if (table.getSelectedRow() != -1) раньше - person mKorbel; 17.04.2012
comment
Эта ошибка очень странная, потому что каждый раз выбирается хотя бы одна строка. Однако я сделал «если», проверив это условие. Возможно, это не лучшее решение, но работает. Спасибо. - person elias; 17.04.2012
comment
Еще одно замечание: всякий раз, когда вы делаете fireTableDateChanged(), он думает, что все изменилось, поэтому сбрасывает выбранную строку. Вместо этого вы должны сделать fireReowAdded/Removed и т.д. - person Reverend Gonzo; 17.04.2012