Observable ArrayList

If you’ve ever needed or wanted to observe an ArrayList for changes, it’s easy to extend it so that it follows the observable pattern.

The Observer

First we’ll make an interface for our observer, which defines all of the methods that we want to observe. Since ArrayList is parameterized, we’ll add a generic parameterization to our observer as well. I’ve added most, if not all, of the methods that modify the list. In your case, you might not need to observe all of these methods, so you can limit the number of methods in your interface to only the ones you need (or extend an abstract class like I do below).

import java.util.Collection;

public interface ArrayListObserver<E> {

	public void onAdd( E element );

	public void onAdd( int index, E element );

	public void onAddAll( Collection<? extends E> elements );

	public void onAddAll( int index, Collection<? extends E> elements );

	public void onClear();

	public void onRemove( int index );

	public void onRemove( Object obj );

	public void onRemoveAll( Collection<?> c );

	public void onRetainAll( Collection<?> c );

	public void onSet( int index, E element );

	public void onSubList( int fromIndex, int toIndex );

}

The Observable

Next we’ll extend ArrayList by making our very own ObservableArrayList. We’ll override all of the methods we want to observe, let the super class do its work, and we’ll notify the observers which method was called.

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

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

	private List<ArrayListObserver<E>> observers = null;
	private static final long serialVersionUID = 1L;

	public ObservableArrayList( ArrayListObserver<E> observer ) {

		registerObserver( observer );

	}

	@Override
	public boolean add( E e ) {

		boolean result = super.add( e );

		if ( result ) {

			for ( ArrayListObserver<E> o : getObservers() ) {

				o.onAdd( e );

			}

		}

		return result;

	}

	@Override
	public void add( int index, E element ) {

		super.add( index, element );

		for ( ArrayListObserver<E> o : getObservers() ) {

			o.onAdd( index, element );

		}

	}

	@Override
	public boolean addAll( Collection<? extends E> c ) {

		boolean result = super.addAll( c );

		if ( result ) {

			for ( ArrayListObserver<E> o : getObservers() ) {

				o.onAddAll( c );

			}

		}

		return result;

	}

	@Override
	public boolean addAll( int index, Collection<? extends E> c ) {

		boolean result = super.addAll( index, c );

		for ( ArrayListObserver<E> o : getObservers() ) {

			o.onAddAll( index, c );

		}

		return result;

	}

	@Override
	public void clear() {

		super.clear();

		for ( ArrayListObserver<E> o : getObservers() ) {

			o.onClear();

		}

	}

	public List<ArrayListObserver<E>> getObservers() {

		if ( observers == null ) {

			observers = new ArrayList<ArrayListObserver<E>>();

		}

		return observers;

	}

	public void registerObserver( ArrayListObserver<E> observer ) {

		getObservers().add( observer );

	}

	@Override
	public E remove( int index ) {

		E toRet = super.remove( index );

		for ( ArrayListObserver<E> o : getObservers() ) {

			o.onRemove( index );

		}

		return toRet;

	}

	@Override
	public boolean remove( Object obj ) {

		boolean result = super.remove( obj );

		if ( result ) {

			for ( ArrayListObserver<E> o : getObservers() ) {

				o.onRemove( obj );

			}

		}

		return result;

	}

	@Override
	public boolean removeAll( Collection<?> c ) {

		boolean result = super.removeAll( c );

		for ( ArrayListObserver<E> o : getObservers() ) {

			o.onRemoveAll( c );

		}

		return result;

	}

	@Override
	public boolean retainAll( Collection<?> c ) {

		boolean result = super.retainAll( c );

		for ( ArrayListObserver<E> o : getObservers() ) {

			o.onRetainAll( c );

		}

		return result;

	}

	@Override
	public E set( int index, E element ) {

		E toRet = super.set( index, element );

		for ( ArrayListObserver<E> o : getObservers() ) {

			o.onSet( index, element );

		}

		return toRet;

	}

	@Override
	public List<E> subList( int fromIndex, int toIndex ) {

		List<E> toRet = super.subList( fromIndex, toIndex );

		for ( ArrayListObserver<E> o : getObservers() ) {

			o.onSubList( fromIndex, toIndex );

		}

		return toRet;

	}

	public void unregisterObserver( ArrayListObserver<E> observer ) {

		getObservers().remove( observer );

	}

}

Abstract Observer

At the moment, for the purpose of this article, I don’t want to observe every method that I specified in my interface. I also don’t want to remove those methods from my interface because in the future I might need them. To get around having to implement each method specified in the interface, I’ll make an abstract observer which, by default, does nothing in each method. Then, I’ll write an implementation extending the abstract class which overrides only the methods I want to observe.

import java.util.Collection;

public abstract class AbstractArrayListObserver<E> implements ArrayListObserver<E> {

	@Override
	public void onAdd( E element ) {

	}

	@Override
	public void onAdd( int index, E element ) {

	}

	@Override
	public void onAddAll( Collection<? extends E> elements ) {

	}

	@Override
	public void onAddAll( int index, Collection<? extends E> elements ) {

	}

	@Override
	public void onClear() {

	}

	@Override
	public void onRemove( int index ) {

	}

	@Override
	public void onRemove( Object obj ) {

	}

	@Override
	public void onRemoveAll( Collection<?> c ) {

	}

	@Override
	public void onRetainAll( Collection<?> c ) {

	}

	@Override
	public void onSet( int index, E element ) {

	}

	@Override
	public void onSubList( int fromIndex, int toIndex ) {

	}

}

Observer Implementation

Here, I just want to observe the add(), remove(), and clear() methods of the ArrayList, so I’ll only override these corresponding methods. For testing purposes, I’m just going to print a message to the console whenever the methods I’m observing are called.

public class MyArrayListObserver<E> extends AbstractArrayListObserver<E> {

	@Override
	public void onAdd( E element ) {

		System.out.println( "Added element: " + element );
		// Do something useful...

	}

	@Override
	public void onClear() {

		System.out.println( "Cleared list!" );
		// Do something useful...

	}

	@Override
	public void onRemove( Object obj ) {

		System.out.println( "Removed object: " + obj );
		// Do something useful...

	}

}

Example Usage

In my main class, I’ll create an ObservableArrayList, register an observer, call some methods on my list and see what the output is.

public class ArrayListObserverTest {

	public static void main( String[] args ) {

		ArrayListObserver<String> observer = new MyArrayListObserver<String>();
		List<String> myList = new ObservableArrayList<String>( observer );
		myList.add( "Test1" );
		myList.remove( "Test1" );
		myList.add( "Test2" );
		myList.clear();

	}

}

Output

You can see in the output that the observer was appropriately notified when each of the observed methods were called.

Added element: Test1
Removed object: Test1
Added element: Test2
Cleared list!
This entry was posted in Java and tagged , . Bookmark the permalink. Follow any comments here with the RSS feed for this post. Post a comment or leave a trackback: Trackback URL.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>