监控集合的更改

时间:2009-02-16 11:49:18

标签: java observer-pattern javabeans

假设你有以下java bean:

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

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

您通常如何为集合实现属性更改事件?您是否尝试使用索引属性,这对于数组而言似乎比集合更多?

8 个答案:

答案 0 :(得分:9)

注意:我在发现自己的一些错误后更新了这篇文章,所以这不是原版,而是更精致的一个)

为此,我会做两个新的接口,ListListenerListenable,然后我会创建一个新的类,如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.");
    }
});

有点混乱,但另一方面,这将允许轻松扩展到集合,集合等等。

答案 1 :(得分:8)

查看Glazed Lists库,它支持可观察的集合。

如果我自己动手,我可能会使用elementsAdded,elementsRemoved方法或类似方法创建自定义Listener接口:-)(也取决于我的需要)

答案 2 :(得分:4)

答案 3 :(得分:2)

通常我会做以下事情:

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作为“旧值”,它会强制触发事件。 (无论如何,听众可能不会真正关心旧的价值)

答案 4 :(得分:1)

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");
    }
}

答案 5 :(得分:0)

对于swing GUI事件,我通常只使用EventListenerList为我做的工作。

编辑:关于问题的改述:你如何处理集合,我通常使用类似于集合类型的事件,所以例如TreeModel事件通常采用TreePath参数,或者地图中的某些东西'd表示关键。

但是对于简单的JavaBeans,最常见的是假设一个列表/数组,只使用索引。

答案 6 :(得分:0)

您需要fireNamesPropertyAddfireNamesProperyDelete。列表级别通知将恕我直言不起作用,即使它是一个数组并添加了索引,因为它无法处理删除。如果某个索引处的元素可以更改,则还需要fireNamesProperyChange。除了字符串值之外,将index作为参数可能很有用。

答案 7 :(得分:0)

您是否正在寻找java.beans.PropertyChangeSupport

在我看来,你应该避免PropertyChangeEventIndexedPropertyChangeEvent更糟糕,而且很少被Swing使用。最好缩小类型的焦点,并触发javax.swing.event.ChangeEvent或类似的(甚至只调用Runnable)。

对于某些类型(如列表!),PeterŠtibraný在另一篇文章中提到的釉面列表(或等效物)似乎是一种很好的方式。