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:
parent
2e08f50b6f
commit
1a806b1f2a
|
@ -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>
|
||||
|
||||
|
|
|
@ -221,6 +221,9 @@
|
|||
<contributor>
|
||||
<name>Guilhem Lavaux</name>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>Paul Legato</name>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>David Leppik</name>
|
||||
</contributor>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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$
|
||||
*
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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");
|
||||
// }
|
||||
|
||||
}
|
Loading…
Reference in New Issue