19

Is there a better way to have a listener on a java collection than wrap it in a class implementing the observer pattern ?

wj.
  • 2,001
  • 3
  • 18
  • 19

5 Answers5

13

You should check out Glazed Lists

It contains observable List classes, which fire events whenever elements are added, removed, replaced, etc

Chi
  • 22,624
  • 6
  • 36
  • 37
  • Glazed Lists' TransformationList expects dispose() to be manually invoked instead of using WeakReferences for listeners....? 0.o There's got to be something better out there. – user515655 Nov 23 '14 at 20:09
6

You can using the ForwardingSet, ForwardingList, etc., from Guava to decorate a particular instance with the desired behavior.

Here's my own implementation that just uses plain JDK APIs:

// create an abstract class that implements this interface with blank implementations
// that way, annonymous subclasses can observe only the events they care about
public interface CollectionObserver<E> {

    public void beforeAdd(E o);

    public void afterAdd(E o);

    // other events to be observed ...

}

// this method would go in a utility class
public static <E> Collection<E> observedCollection(
    final Collection<E> collection, final CollectionObserver<E> observer) {
        return new Collection<E>() {
            public boolean add(final E o) {
                observer.beforeAdd(o);
                boolean result = collection.add(o);
                observer.afterAdd(o);
                return result;
            }

            // ... generate rest of delegate methods in Eclipse

    };
    }
Matt Sgarlata
  • 1,761
  • 1
  • 16
  • 13
les2
  • 14,093
  • 16
  • 59
  • 76
2

Apache Events.

"Commons-Events provides additional classes for firing and handling events. It focusses on the Java Collections Framework, providing decorators to other collections that fire events."

Martin Schröder
  • 4,176
  • 7
  • 47
  • 81
John Ellinwood
  • 14,291
  • 7
  • 38
  • 48
1

Well, if you don't actually need a java.util.Collection or List instance, you could use a DefaultListModel. I'm not aware of any "real" Collection implementations with builtin listener/observer support.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
  • You linked the Java 6 version of DefaultListModel, which does not use Generics. The [Java 7 version](http://docs.oracle.com/javase/7/docs/api/javax/swing/DefaultListModel.html) does, which might make your suggestion more attractive. – Martin Rust Nov 15 '16 at 14:32
  • @MartinRust well, yeah, the answer is from 2 years before Java 7 came out. If I'm gonna update it I might as well use Java 8 now – Michael Borgwardt Nov 15 '16 at 15:02
0

there are many ways to achieve this - often i use this approach

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class ObservableArrayList<E> extends ArrayList<E> {

    private @interface MethodId {
         private static final int REMOVE = 2;
         private static final int ADD = 1;
    }

    public interface ListObserver<E> {
        void onElementAdded(E element);
        void onElementRemoved(E element);
    }

    public ObservableArrayList(int capacity) {
        super(capacity);
        ensureObserver();
    }

    public ObservableArrayList() {
        ensureObserver();
    }

    public ObservableArrayList(Collection<? extends E> collection) {
        super(collection);
        ensureObserver();
    }

    private List<WeakReference<ListObserver<E>>> _listObserverWeakRefList;

    public void addObserver(ListObserver<E> observer) {
         _listObserverWeakRefList.add(new WeakReference<ListObserver<E>>   (observer));
    }

    private void ensureObserver() {
       if (_listObserverWeakRefList == null) {
            _listObserverWeakRefList = new ArrayList<>();
       }
    }

    @Override
    public boolean add(E object) {
        super.add(object);
        callObservable(MethodId.ADD, object);
        return true;
    }

    @Override
    public boolean remove(Object object) {
        boolean removed = super.remove(object);
        if (removed) callObservable(MethodId.REMOVE, object);
        return removed;
    }

    private void callObservable(@MethodId int methodId, Object element) {
         for (WeakReference<ListObserver<E>> observerRef :  _listObserverWeakRefList) {
             ListObserver<E> observer = observerRef.get();
             if (observer != null) {
                 switch (methodId) {
                    case MethodId.ADD:
                        observer.onElementAdded((E) element);
                        break;
                    case MethodId.REMOVE:
                        observer.onElementRemoved((E) element);
                        break;
                } 
            }
        }
    }

}
ceph3us
  • 7,326
  • 3
  • 36
  • 43