Add GrowthList, an auto-grow on set/add list

bug 34171, including code from Paul Legato

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@170244 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stephen Colebourne 2005-05-15 18:30:49 +00:00
parent 2e08f50b6f
commit 1a806b1f2a
6 changed files with 370 additions and 0 deletions

View File

@ -43,6 +43,7 @@ If this causes major headaches to anyone please contact commons-dev at jakarta.a
<center><h3>NEW CLASSES</h3></center>
<ul>
<li>DefaultedMap - Returns a default value when the key is not found, without adding the default value to the map itself [30911]</li>
<li>GrowthList - Decorator that causes set and indexed add to expand the list rather than throw IndexOutOfBoundsException [34171]</li>
<li>LoopingListIterator - When the end of the list is reached the iteration continues from the start [30166]</li>
</ul>

View File

@ -221,6 +221,9 @@
<contributor>
<name>Guilhem Lavaux</name>
</contributor>
<contributor>
<name>Paul Legato</name>
</contributor>
<contributor>
<name>David Leppik</name>
</contributor>

View File

@ -0,0 +1,185 @@
/*
* Copyright 2005 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.collections.list;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Decorates another <code>List</code> to make it seemlessly grow when
* indices larger than the list size are used on add and set,
* avoiding most IndexOutOfBoundsExceptions.
* <p>
* This class avoids errors by growing when a set or add method would
* normally throw an IndexOutOfBoundsException.
* Note that IndexOutOfBoundsException IS returned for invalid negative indices.
* <p>
* Trying to set or add to an index larger than the size will cause the list
* to grow (using <code>null</code> elements). Clearly, care must be taken
* not to use excessively large indices, as the internal list will grow to
* match.
* <p>
* Trying to use any method other than add or set with an invalid inde will
* call the underlying list and probably result in an IndexOutOfBoundsException.
* <p>
* Take care when using this list with <code>null</code> values, as
* <code>null</code> is the value added when growing the list.
* <p>
* All sub-lists will access the underlying list directly, and will throw
* IndexOutOfBoundsExceptions.
* <p>
* This class differs from {@link LazyList} because here growth occurs on
* set and add, where <code>LazyList</code> grows on get. However, they
* could easily be used together by decorating twice.
*
* @see LazyList
* @since Commons Collections 3.2
* @version $Revision: 155406 $ $Date$
*
* @author Stephen Colebourne
* @author Paul Legato
*/
public class GrowthList extends AbstractSerializableListDecorator {
/** Serialization version */
private static final long serialVersionUID = -3620001881672L;
/**
* Factory method to create a growth list.
*
* @param list the list to decorate, must not be null
* @throws IllegalArgumentException if list is null
*/
public static List decorate(List list) {
return new GrowthList(list);
}
//-----------------------------------------------------------------------
/**
* Constructor that uses an ArrayList internally.
*/
public GrowthList() {
super(new ArrayList());
}
/**
* Constructor that uses an ArrayList internally.
*
* @param initialSize the initial size of the ArrayList
* @throws IllegalArgumentException if initial size is invalid
*/
public GrowthList(int initialSize) {
super(new ArrayList(initialSize));
}
/**
* Constructor that wraps (not copies).
*
* @param list the list to decorate, must not be null
* @throws IllegalArgumentException if list is null
*/
protected GrowthList(List list) {
super(list);
}
//-----------------------------------------------------------------------
/**
* Decorate the add method to perform the growth behaviour.
* <p>
* If the requested index is greater than the current size, the list will
* grow to the new size. Indices between the old size and the requested
* size will be filled with <code>null</code>.
* <p>
* If the index is less than the current size, the value will be added to
* the underlying list directly.
* If the index is less than zero, the underlying list is called, which
* will probably throw an IndexOutOfBoundsException.
*
* @param index the index to add at
* @param element the object to add at the specified index
* @throws UnsupportedOperationException if the underlying list doesn't implement set
* @throws ClassCastException if the underlying list rejects the element
* @throws IllegalArgumentException if the underlying list rejects the element
*/
public void add(int index, Object element) {
int size = getList().size();
if (index > size) {
getList().addAll(Collections.nCopies(index - size, null));
}
getList().add(index, element);
}
//-----------------------------------------------------------------------
/**
* Decorate the addAll method to perform the growth behaviour.
* <p>
* If the requested index is greater than the current size, the list will
* grow to the new size. Indices between the old size and the requested
* size will be filled with <code>null</code>.
* <p>
* If the index is less than the current size, the values will be added to
* the underlying list directly.
* If the index is less than zero, the underlying list is called, which
* will probably throw an IndexOutOfBoundsException.
*
* @param index the index to add at
* @param coll the collection to add at the specified index
* @return true if the list changed
* @throws UnsupportedOperationException if the underlying list doesn't implement set
* @throws ClassCastException if the underlying list rejects the element
* @throws IllegalArgumentException if the underlying list rejects the element
*/
public boolean addAll(int index, Collection coll) {
int size = getList().size();
boolean result = false;
if (index > size) {
getList().addAll(Collections.nCopies(index - size, null));
result = true;
}
return (getList().addAll(index, coll) | result);
}
//-----------------------------------------------------------------------
/**
* Decorate the set method to perform the growth behaviour.
* <p>
* If the requested index is greater than the current size, the list will
* grow to the new size. Indices between the old size and the requested
* size will be filled with <code>null</code>.
* <p>
* If the index is less than the current size, the value will be set onto
* the underlying list directly.
* If the index is less than zero, the underlying list is called, which
* will probably throw an IndexOutOfBoundsException.
*
* @param index the index to set
* @param element the object to set at the specified index
* @return the object previously at that index
* @throws UnsupportedOperationException if the underlying list doesn't implement set
* @throws ClassCastException if the underlying list rejects the element
* @throws IllegalArgumentException if the underlying list rejects the element
*/
public Object set(int index, Object element) {
int size = getList().size();
if (index >= size) {
getList().addAll(Collections.nCopies((index - size) + 1, null));
}
return getList().set(index, element);
}
}

View File

@ -46,8 +46,13 @@ import org.apache.commons.collections.Factory;
* instance is the fourth element in the list. The first, second,
* and third element are all set to <code>null</code>.
* <p>
* This class differs from {@link GrowthList} because here growth occurs on
* get, where <code>GrowthList</code> grows on set and add. However, they
* could easily be used together by decorating twice.
* <p>
* This class is Serializable from Commons Collections 3.1.
*
* @see GrowthList
* @since Commons Collections 3.0
* @version $Revision$ $Date$
*

View File

@ -46,6 +46,7 @@ public class TestAll extends TestCase {
suite.addTest(TestTreeList.suite());
suite.addTest(TestFixedSizeList.suite());
suite.addTest(TestGrowthList.suite());
suite.addTest(TestPredicatedList.suite());
suite.addTest(TestSetUniqueList.suite());
suite.addTest(TestSynchronizedList.suite());

View File

@ -0,0 +1,175 @@
/*
* Copyright 2005 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.collections.list;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import junit.framework.Test;
import junit.framework.TestSuite;
/**
* Extension of {@link TestList} for exercising the {@link GrowthList}.
*
* @since Commons Collections 3.2
* @version $Revision: 155406 $ $Date$
*
* @author Stephen Colebourne
*/
public class TestGrowthList extends AbstractTestList {
public TestGrowthList(String testName) {
super(testName);
}
public static Test suite() {
return new TestSuite(TestGrowthList.class);
}
public static void main(String args[]) {
String[] testCaseName = { TestGrowthList.class.getName()};
junit.textui.TestRunner.main(testCaseName);
}
public List makeEmptyList() {
return new GrowthList();
}
public List makeFullList() {
List list = new ArrayList();
list.addAll(Arrays.asList(getFullElements()));
return GrowthList.decorate(list);
}
//-----------------------------------------------------------------------
public void testGrowthAdd() {
Integer one = new Integer(1);
GrowthList grower = new GrowthList();
assertEquals(0, grower.size());
grower.add(1, one);
assertEquals(2, grower.size());
assertEquals(null, grower.get(0));
assertEquals(one, grower.get(1));
}
public void testGrowthAddAll() {
Integer one = new Integer(1);
Integer two = new Integer(2);
Collection coll = new ArrayList();
coll.add(one);
coll.add(two);
GrowthList grower = new GrowthList();
assertEquals(0, grower.size());
grower.addAll(1, coll);
assertEquals(3, grower.size());
assertEquals(null, grower.get(0));
assertEquals(one, grower.get(1));
assertEquals(two, grower.get(2));
}
public void testGrowthSet1() {
Integer one = new Integer(1);
GrowthList grower = new GrowthList();
assertEquals(0, grower.size());
grower.set(1, one);
assertEquals(2, grower.size());
assertEquals(null, grower.get(0));
assertEquals(one, grower.get(1));
}
public void testGrowthSet2() {
Integer one = new Integer(1);
GrowthList grower = new GrowthList();
assertEquals(0, grower.size());
grower.set(0, one);
assertEquals(1, grower.size());
assertEquals(one, grower.get(0));
}
//-----------------------------------------------------------------------
/**
* Override.
*/
public void testListAddByIndexBoundsChecking() {
List list;
Object element = getOtherElements()[0];
try {
list = makeEmptyList();
list.add(-1, element);
fail("List.add should throw IndexOutOfBoundsException [-1]");
} catch (IndexOutOfBoundsException e) {
// expected
}
}
/**
* Override.
*/
public void testListAddByIndexBoundsChecking2() {
List list;
Object element = getOtherElements()[0];
try {
list = makeFullList();
list.add(-1, element);
fail("List.add should throw IndexOutOfBoundsException [-1]");
} catch (IndexOutOfBoundsException e) {
// expected
}
}
/**
* Override.
*/
public void testListSetByIndexBoundsChecking() {
List list = makeEmptyList();
Object element = getOtherElements()[0];
try {
list.set(-1, element);
fail("List.set should throw IndexOutOfBoundsException [-1]");
} catch (IndexOutOfBoundsException e) {
// expected
}
}
/**
* Override.
*/
public void testListSetByIndexBoundsChecking2() {
List list = makeFullList();
Object element = getOtherElements()[0];
try {
list.set(-1, element);
fail("List.set should throw IndexOutOfBoundsException [-1]");
} catch(IndexOutOfBoundsException e) {
// expected
}
}
//-----------------------------------------------------------------------
public String getCompatibilityVersion() {
return "3.2";
}
// public void testCreate() throws Exception {
// resetEmpty();
// writeExternalFormToDisk((java.io.Serializable) collection, "C:/commons/collections/data/test/GrowthList.emptyCollection.version3.2.obj");
// resetFull();
// writeExternalFormToDisk((java.io.Serializable) collection, "C:/commons/collections/data/test/GrowthList.fullCollection.version3.2.obj");
// }
}