LazyList based on the Google Collections Library
Published by peter October 1st, 2007 in java.In my previous post I mentioned implementing the Google Collections counterpart of the commons-collections LazyList. I'll refer to the Google Collections Library as GCL from now on.
The LazyList decorates another List to create objects in the list on demand. To code below is loosly based on the code in commond-collections' lazylist counterpart. Most modifications are generics related.
Since GCL supplies a 'Forwarding' wrappers it's really easy to write a decorator for the List type. Extend from ForwardingList and override the needed methods:
-
package com.finalist.collections.lazy;
-
-
import java.util.ArrayList;
-
import java.util.List;
-
-
import com.finalist.collections.factories.Factory;
-
import com.google.common.collect.ForwardingList;
-
-
/**
-
* Decorates another <code>List</code> to create objects in the list on
-
* demand. Code loosly based on the code in commond-collections' lazylist
-
* counterpart. Using the forwardinglist provided by Googles' Collection library
-
*
-
* When the {@link #get(int)} method is called with an index greater than the
-
* size of the list, the list will automatically grow in size and return a new
-
* object from the specified factory. The gaps will be filled by null. If a get
-
* method call encounters a null, it will be replaced with a new object from the
-
* factory. Thus this list is unsuitable for storing null objects.
-
*
-
* @author Peter Maas
-
*
-
* @param <T> Type of the objects contained in the list
-
*/
-
public class LazyList<T> extends ForwardingList<T> {
-
private static final long serialVersionUID = 1;
-
private Factory<T> factory;
-
-
protected LazyList(Factory<T> factory) {
-
super(new ArrayList<T>());
-
this.factory = factory;
-
}
-
-
protected LazyList(List<T> delegate, Factory<T> factory) {
-
super(delegate);
-
this.factory = factory;
-
}
-
-
public static <D> LazyList<D> decorate(List<D> delegate, Factory<D> factory) {
-
return new LazyList<D>(delegate, factory);
-
}
-
-
/*
-
* (non-Javadoc)
-
*
-
* @see com.google.common.collect.ForwardingList#get(int)
-
*/
-
public T get(int index) {
-
int size = delegate().size();
-
if (index <size) {
-
// within bounds, get the object
-
T object = delegate().get(index);
-
if (object == null) {
-
// item is a place holder, create new one, set and return
-
object = factory.create();
-
delegate().set(index, object);
-
return object;
-
} else {
-
// good and ready to go
-
return object;
-
}
-
} else {
-
// we have to grow the list
-
for (int i = size; i <index; i++) {
-
delegate().add(null);
-
}
-
// create our last object, set and return
-
T object = factory.create();
-
delegate().add(object);
-
return object;
-
}
-
}
-
-
/*
-
* (non-Javadoc)
-
*
-
* @see com.google.common.collect.ForwardingList#subList(int, int)
-
*/
-
public List<T> subList(int fromIndex, int toIndex) {
-
List<T> sub = delegate().subList(fromIndex, toIndex);
-
return new LazyList<T>(sub, factory);
-
}
-
}
As you can see I modified code from the commons-collections counterpart to use generics. I reimplemented parts of commons-collections' factory structure to use generics as well.
The next test concludes in success:
-
package com.finalist.collections.lazy;
-
-
import java.util.List;
-
import com.finalist.collections.factories.FactoryUtils;
-
import junit.framework.TestCase;
-
-
public class LazyListTest extends TestCase {
-
-
/**
-
* Test for the get(int) method of the LazyList
-
*/
-
public void testGetInt() {
-
List<Dummy> dummyList = new LazyList<Dummy>(FactoryUtils.instantiateFactory(Dummy.class));
-
-
// initially the list should be empty
-
assertEquals(0, dummyList.size());
-
-
// accessing indexes directly should not result in index-out-of-bounds exceptions
-
assertNotNull(dummyList.get(0));
-
assertNotNull(dummyList.get(10));
-
-
// size should now be 11:
-
assertEquals(11, dummyList.size());
-
-
// the spaces between the to requested indexes should be populated with
-
// null values:
-
assertTrue(dummyList.contains(null));
-
assertEquals(1, dummyList.indexOf(null));
-
}
-
-
}
Please note that the above isn't a showcase for the features of GCL, its' a simple extension to add a LazyList implementation. Feel free to use or extend it!
full sources, test and buildfile




















Regarding
// we have to grow the list
for (int i = size; i
Right, my message was truncated!
My point was: when you are growing the list, you may want to use the potentially more efficient addAll. In particular when you're adding to an ArrayList, the array can be resized in advance. Growing arrays is often a big performance hit in Java programs.
You only need to device some smart immutable collection that returns the right amount of nulls.
Otherwise: nice post (sorry, forgot to say that in the other comment).