Отслеживать изменения в коллекции - PullRequest
6 голосов
/ 16 февраля 2009

Скажем, у вас есть следующий боб Java:

public class MyBean
{
    private List<String> names = new ArrayList<String>();

    public void addName(String name)
    {
        names.add(name);
        fireNamesPropertyChange(name);
    }
}

Как бы вы обычно реализовывали событие изменения свойства для коллекции? Пытаетесь ли вы использовать свойство индекса, которое больше подходит для массивов, чем коллекций?

Ответы [ 8 ]

9 голосов
/ 16 февраля 2009

( ПРИМЕЧАНИЕ : я обновил этот пост после того, как осознал несколько собственных ошибок, так что это не оригинал, а более изощренная)

Для этой цели я бы сделал два новых интерфейса, ListListener и Listenable, а затем я бы создал новый класс, такой как ListenableArrayList, который обернул бы каждый List метод вызовом одного (или более). соответствующие методы определены в ListListener. В коде это будет примерно так:

public class ListenableArrayList<T> extends ArrayList<T>
                                    implements Listenable<T> {

    private ArrayList<T> internalList;
    private ListListener<T> listener;

    /* .. */

    public void add(T item) {
        listener.beforeAdd(T item);
        internalList.add(item);
        listener.afterAdd(T item);
    }

    /* .. */

    public void setListener(ListListener<T> listener) {
        this.listener = listener;
    }

}

public interface ListListener<T> {
    /* .. */
    void beforeAdd(T item);
    void afterAdd(T item);
    /* .. */
}

public interface Listenable<T> {
    /* .. */
    void setListener(ListListener<T> listener);
    /* .. */
}

Причина, по которой я бы сделал это таким образом, состояла в том, чтобы позволить создавать действительно специальные слушатели на лету вместо привязки ListenableArrayList к какой-то конкретной реализации. Например, при этом возможно следующее:

Listenable<String> list = new ListenableArrayList<String>();

list.setListener(new ListListener<String>() {
    @Override
    public void beforeAdd(String item) {
        System.out.println("About to add element "+item+"...");
    }
    @Override
    public void afterAdd(String item) {
        System.out.println("...element "+item+" has been added.");
    }
});

Немного загромождено, может быть, но, с другой стороны, это позволило бы легко расширить Коллекции, Наборы и еще много чего.

8 голосов
/ 16 февраля 2009

Взгляните на библиотеку Glazed Lists , которая поддерживает наблюдаемые коллекции.

Если бы я делал это сам, я бы, вероятно, создал бы пользовательский интерфейс Listener с элементами elementsAdded, elementsRemoved или аналогичными :-) (также в зависимости от моих потребностей)

4 голосов
/ 16 февраля 2009

Вы можете использовать Наблюдаемую Коллекцию: https://commons.apache.org/dormant/events/apidocs/org/apache/commons/events/observable/ObservableCollection.html

2 голосов
/ 18 февраля 2009

Обычно я делал бы следующее:

public class MyBean {
    private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
    private List<String> names = new ArrayList<String>();
    public void addName(String name) {
        names.add(name);
        pcs.firePropertyChange("names", null, Collections.unmodifiableList(names));
    }
    public void addPropertyChangeListener(PropertyChangeListener l) {
        pcs.addPropertyChangeListener(l);
    }
    public void removePropertyChangeListener(PropertyChangeListener l) {
        pcs.removePropertyChangeListener(l);
    }
}

PropertyChangeSupport управляет слушателями и запускает события от вашего имени.

Передавая null как «старое значение», оно вызывает событие. (Вполне вероятно, что слушатели все равно не будут заботиться о старом значении)

1 голос
/ 24 октября 2013

JDK 7+ решение:

import javafx.collections.*;
import java.util.*;

public class Test {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();

        list.add("s1");
        list.add("s2");

        ObservableList<String> observableList = FXCollections.observableList(list);
        observableList.addListener(new ListChangeListener<String>() {
            @Override
            public void onChanged(Change<? extends String> change) {
                while(change.next()){
                    System.out.println("added: " + change.getAddedSubList());
                }
            }
        });

        observableList.add("s3");
    }
}
0 голосов
/ 16 февраля 2009

Возможно, вы ищете java.beans.PropertyChangeSupport?

На мой взгляд, вам следует избегать PropertyChangeEvent. IndexedPropertyChangeEvent хуже и очень редко используется Swing в любом случае. Лучше сузить фокус ваших типов и запустить javax.swing.event.ChangeEvent или подобное (даже просто позвоните Runnable).

Для некоторых типов (например, списки!), Глазурованные списки (или эквивалентные), упомянутые в другом сообщении Питера Штибраны, кажутся хорошим способом.

0 голосов
/ 16 февраля 2009

Метинкс вам понадобится fireNamesPropertyAdd, fireNamesProperyDelete. Уведомление на уровне списка IMHO не будет работать, даже если это был массив и был добавлен индекс, так как он не может обрабатывать удаления. Если элемент по какому-либо индексу можно изменить, вам также понадобится fireNamesProperyChange. Может быть полезно иметь индекс в качестве параметра в дополнение к строковому значению.

0 голосов
/ 16 февраля 2009

Для события с графическим пользовательским интерфейсом я обычно просто использую EventListenerList , чтобы выполнить работу за меня.

РЕДАКТИРОВАТЬ: перефразируя вопросы: как вы относитесь к коллекциям, я бы обычно использовал событие, подобное типу коллекций, поэтому, например, событие TreeModel обычно принимает аргумент TreePath или что-то в карте, которую я указал бы ключ.

Однако для простых JavaBean-компонентов наиболее распространенным является использование списка / массива и просто использование индекса.

...