Hi there,
I never really worked with weak references before. Now I created an ObserverList and used WeakReference to solve the Lapsed Observer problem.
However, I am not quite sure if I did it right. Unfortunately these kinds of classes are not that easy to test.
I would really appreciate some input from more experienced programmers on this.
As a backing List I use a CopyOnWriteArrayList. I also use a custom iterator to filter out lapsed observers.
Lapsed observers _should_ (if I implemented this correctly) be filtered out everytime the list is iterated over.
import java.lang.ref.WeakReference; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; public class CowalObsList<E> implements ObsList<E> { /** * We use a copy on write array list to make out observer list thread safe and to protect us against ConcurrentModificationException's. * * @see CopyOnWriteArrayList * @see ConcurrentModificationException */ private final CopyOnWriteArrayList<WeakReference<E>> list = new CopyOnWriteArrayList<>(); /** * Adds the given observer to this observer list.<br> * This method will not result in a ConcurrentModificationException even if an iteration is currently taking place.<br> * The added observer will be included in the next iteration over this observer list. * * @param obs * the observer to be added * @throws NullPointerException * if obs is null * @see ConcurrentModificationException */ public void addObserver(E obs) { if (obs == null) { throw new NullPointerException(); } WeakReference<E> ref = new WeakReference<>(obs); list.add(ref); } /** * Removes the observer from this observer list if it is contained.<br> * This method will not result in a ConcurrentModificationException even if an iteration is currently taking place.<br> * If the observer is not contained in this observer list this method does nothing. * * @param obs * the observer to be removed * @throws NullPointerException * if obs is null * @see ConcurrentModificationException */ public void removeObserver(E obs) { if (obs == null) { throw new NullPointerException(); } for (WeakReference<E> ref : list) { if (ref.get() == obs || ref.get() == null) { list.remove(ref); } } } /** * Returns an iterator over the elements in this observer list in a non defined sequence.<br> * The returned iterator provides a snapshot of the state of the list when the iterator was constructed.<br> * No synchronization is needed while traversing the iterator. The iterator does NOT support the remove method. * * @see CopyOnWriteArrayList#iterator() */ public Iterator<E> iterator() { return new CowalObsListIterator<>(this); } /** * This custom Iterator is used to filter out lapsed observers from this observer list.<br> * It also makes sure that the user does not have to deal with WeakReference's. */ private static class CowalObsListIterator<E> implements Iterator<E> { /** * The CowalObserverList that created this iterator. */ final CowalObsList<E> cowalList; /** * An iterator of the List that is backing the cowalList. */ final Iterator<WeakReference<E>> delegate; /** * The next reference from which to return the referent in the {@link #next()} method. */ WeakReference<E> next; public CowalObsListIterator(CowalObsList<E> source) { cowalList = source; delegate = cowalList.list.iterator(); next = null; } public boolean hasNext() { // Filter out lapsed observers while (next == null && delegate.hasNext()) { WeakReference<E> ref = delegate.next(); if (ref.get() == null) { // The reference is referring to a lapsed observer and must be removed from the backing list. cowalList.list.remove(ref); } else { // Breaks the loop. next = ref; } } return next != null; } public E next() { E element = next.get(); // Important to set next to null after a call to the next() method! next = null; return element; } } }
Thank you for your time.