COLLECTIONS-426 patch applied.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1377491 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Brent Worden 2012-08-26 19:29:47 +00:00
parent 38bb9b4ac5
commit b10fa43d3e
3 changed files with 163 additions and 80 deletions

View File

@ -78,6 +78,9 @@
<action issue="COLLECTIONS-427" dev="brentworden" type="fix" due-to="Mert Guldur">
Fixed performance issue in "SetUniqueList#retainAll" method for large collections.
</action>
<action issue="COLLECTIONS-426" dev="brentworden" type="fix" due-to="Adrian Nistor">
Fixed performance issue in "ListOrderedSet#retainAll" method for large collections.
</action>
</release>
</body>
</document>

View File

@ -29,27 +29,30 @@ import org.apache.commons.collections.iterators.AbstractIteratorDecorator;
import org.apache.commons.collections.list.UnmodifiableList;
/**
* Decorates another <code>Set</code> to ensure that the order of addition
* is retained and used by the iterator.
* Decorates another <code>Set</code> to ensure that the order of addition is
* retained and used by the iterator.
* <p>
* If an object is added to the set for a second time, it will remain in the
* original position in the iteration.
* The order can be observed from the set via the iterator or toArray methods.
* original position in the iteration. The order can be observed from the set
* via the iterator or toArray methods.
* <p>
* The ListOrderedSet also has various useful direct methods. These include many
* from <code>List</code>, such as <code>get(int)</code>, <code>remove(int)</code>
* and <code>indexOf(int)</code>. An unmodifiable <code>List</code> view of
* the set can be obtained via <code>asList()</code>.
* from <code>List</code>, such as <code>get(int)</code>,
* <code>remove(int)</code> and <code>indexOf(int)</code>. An unmodifiable
* <code>List</code> view of the set can be obtained via <code>asList()</code>.
* <p>
* This class cannot implement the <code>List</code> interface directly as
* various interface methods (notably equals/hashCode) are incompatible with a set.
* various interface methods (notably equals/hashCode) are incompatible with a
* set.
* <p>
* This class is Serializable from Commons Collections 3.1.
*
*
* @since 3.0
* @version $Id$
*/
public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> implements Set<E> {
public class ListOrderedSet<E>
extends AbstractSerializableSetDecorator<E>
implements Set<E> {
/** Serialization version */
private static final long serialVersionUID = -228664372470420141L;
@ -58,13 +61,14 @@ public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> imple
protected final List<E> setOrder;
/**
* Factory method to create an ordered set specifying the list and set to use.
* Factory method to create an ordered set specifying the list and set to
* use.
* <p>
* The list and set must both be empty.
*
*
* @param <E> the element type
* @param set the set to decorate, must be empty and not null
* @param list the list to decorate, must be empty and not null
* @param set the set to decorate, must be empty and not null
* @param list the list to decorate, must be empty and not null
* @return a new ordered set
* @throws IllegalArgumentException if set or list is null
* @throws IllegalArgumentException if either the set or list is not empty
@ -87,9 +91,9 @@ public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> imple
* Factory method to create an ordered set.
* <p>
* An <code>ArrayList</code> is used to retain order.
*
*
* @param <E> the element type
* @param set the set to decorate, must not be null
* @param set the set to decorate, must not be null
* @return a new ordered set
* @throws IllegalArgumentException if set is null
*/
@ -98,15 +102,16 @@ public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> imple
}
/**
* Factory method to create an ordered set using the supplied list to retain order.
* Factory method to create an ordered set using the supplied list to retain
* order.
* <p>
* A <code>HashSet</code> is used for the set behaviour.
* <p>
* NOTE: If the list contains duplicates, the duplicates are removed,
* altering the specified list.
*
*
* @param <E> the element type
* @param list the list to decorate, must not be null
* @param list the list to decorate, must not be null
* @return a new ordered set
* @throws IllegalArgumentException if list is null
*/
@ -120,11 +125,11 @@ public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> imple
return new ListOrderedSet<E>(set, list);
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
/**
* Constructs a new empty <code>ListOrderedSet</code> using
* a <code>HashSet</code> and an <code>ArrayList</code> internally.
*
* Constructs a new empty <code>ListOrderedSet</code> using a
* <code>HashSet</code> and an <code>ArrayList</code> internally.
*
* @since 3.1
*/
public ListOrderedSet() {
@ -134,8 +139,8 @@ public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> imple
/**
* Constructor that wraps (not copies).
*
* @param set the set to decorate, must not be null
*
* @param set the set to decorate, must not be null
* @throws IllegalArgumentException if set is null
*/
protected ListOrderedSet(Set<E> set) {
@ -144,12 +149,13 @@ public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> imple
}
/**
* Constructor that wraps (not copies) the Set and specifies the list to use.
* Constructor that wraps (not copies) the Set and specifies the list to
* use.
* <p>
* The set and list must both be correctly initialised to the same elements.
*
* @param set the set to decorate, must not be null
* @param list the list to decorate, must not be null
*
* @param set the set to decorate, must not be null
* @param list the list to decorate, must not be null
* @throws IllegalArgumentException if set or list is null
*/
protected ListOrderedSet(Set<E> set, List<E> list) {
@ -160,17 +166,17 @@ public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> imple
setOrder = list;
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
/**
* Gets an unmodifiable view of the order of the Set.
*
*
* @return an unmodifiable list view
*/
public List<E> asList() {
return UnmodifiableList.unmodifiableList(setOrder);
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
@Override
public void clear() {
collection.clear();
@ -220,20 +226,26 @@ public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> imple
@Override
public boolean retainAll(Collection<?> coll) {
boolean result = collection.retainAll(coll);
if (result == false) {
Set<Object> collectionRetainAll = new HashSet<Object>();
for (Iterator<?> it = coll.iterator(); it.hasNext();) {
Object next = it.next();
if (collection.contains(next)) {
collectionRetainAll.add(next);
}
}
if (collectionRetainAll.size() == collection.size()) {
return false;
}
if (collection.size() == 0) {
setOrder.clear();
if (collectionRetainAll.size() == 0) {
clear();
} else {
for (Iterator<E> it = setOrder.iterator(); it.hasNext();) {
if (!collection.contains(it.next())) {
for (Iterator<E> it = iterator(); it.hasNext();) {
if (!collectionRetainAll.contains(it.next())) {
it.remove();
}
}
}
return result;
return true;
}
@Override
@ -246,13 +258,13 @@ public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> imple
return setOrder.toArray(a);
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// Additional methods that comply to the {@link List} interface
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
/**
* Returns the element at the specified position in this ordered set.
*
*
* @param index the position of the element in the ordered {@link Set}.
* @return the element at position {@code index}
* @see List#get(int)
@ -262,11 +274,12 @@ public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> imple
}
/**
* Returns the index of the first occurrence of the specified element in ordered set.
* Returns the index of the first occurrence of the specified element in
* ordered set.
*
* @param object the element to search for
* @return the index of the first occurrence of the object, or {@code -1} if this
* ordered set does not contain this object
* @return the index of the first occurrence of the object, or {@code -1} if
* this ordered set does not contain this object
* @see List#indexOf(Object)
*/
public int indexOf(Object object) {
@ -274,10 +287,10 @@ public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> imple
}
/**
* Inserts the specified element at the specified position if it is not yet contained in this
* ordered set (optional operation). Shifts the element currently at this position and any
* subsequent elements to the right.
*
* Inserts the specified element at the specified position if it is not yet
* contained in this ordered set (optional operation). Shifts the element
* currently at this position and any subsequent elements to the right.
*
* @param index the index at which the element is to be inserted
* @param object the element to be inserted
* @see List#add(int, Object)
@ -290,9 +303,10 @@ public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> imple
}
/**
* Inserts all elements in the specified collection not yet contained in the ordered set at the specified
* position (optional operation). Shifts the element currently at the position and all subsequent
* elements to the right.
* Inserts all elements in the specified collection not yet contained in the
* ordered set at the specified position (optional operation). Shifts the
* element currently at the position and all subsequent elements to the
* right.
*
* @param index the position to insert the elements
* @param coll the collection containing the elements to be inserted
@ -311,7 +325,7 @@ public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> imple
toAdd.add(e);
changed = true;
}
if (changed) {
setOrder.addAll(index, toAdd);
}
@ -320,8 +334,8 @@ public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> imple
}
/**
* Removes the element at the specified position from the ordered set. Shifts any subsequent
* elements to the left.
* Removes the element at the specified position from the ordered set.
* Shifts any subsequent elements to the left.
*
* @param index the index of the element to be removed
* @return the element that has been remove from the ordered set
@ -334,9 +348,9 @@ public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> imple
}
/**
* Uses the underlying List's toString so that order is achieved.
* This means that the decorated Set's toString is not used, so
* any custom toStrings will be ignored.
* Uses the underlying List's toString so that order is achieved. This means
* that the decorated Set's toString is not used, so any custom toStrings
* will be ignored.
*
* @return a string representation of the ordered set
*/
@ -346,11 +360,13 @@ public class ListOrderedSet<E> extends AbstractSerializableSetDecorator<E> imple
return setOrder.toString();
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
/**
* Internal iterator handle remove.
*/
static class OrderedSetIterator<E> extends AbstractIteratorDecorator<E> implements OrderedIterator<E> {
static class OrderedSetIterator<E>
extends AbstractIteratorDecorator<E>
implements OrderedIterator<E> {
/** Object we iterate on */
protected final Collection<E> set;

View File

@ -17,23 +17,28 @@
package org.apache.commons.collections.set;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* Extension of {@link AbstractSetTest} for exercising the {@link ListOrderedSet}
* implementation.
*
* Extension of {@link AbstractSetTest} for exercising the
* {@link ListOrderedSet} implementation.
*
* @since 3.0
* @version $Id$
*/
public class ListOrderedSetTest<E> extends AbstractSetTest<E> {
public class ListOrderedSetTest<E>
extends AbstractSetTest<E> {
private static final Integer ZERO = new Integer(0);
private static final Integer ONE = new Integer(1);
private static final Integer TWO = new Integer(2);
private static final Integer THREE = new Integer(3);
public ListOrderedSetTest(String testName) {
@ -65,12 +70,14 @@ public class ListOrderedSetTest<E> extends AbstractSetTest<E> {
}
for (int i = 0; i < 10; i += 2) {
assertTrue("Must be able to remove int", set.remove(Integer.toString(i)));
assertTrue("Must be able to remove int",
set.remove(Integer.toString(i)));
}
it = set.iterator();
for (int i = 1; i < 10; i += 2) {
assertEquals("Sequence is wrong after remove ", Integer.toString(i), it.next());
assertEquals("Sequence is wrong after remove ",
Integer.toString(i), it.next());
}
for (int i = 0; i < 10; i++) {
@ -147,7 +154,7 @@ public class ListOrderedSetTest<E> extends AbstractSetTest<E> {
assertSame(TWO, set.get(2));
list.add(0, (E) THREE); // list = [3,0,2]
set.remove(TWO); // set = [0,1]
set.remove(TWO); // set = [0,1]
set.addAll(1, list);
assertEquals(4, set.size());
assertSame(ZERO, set.get(0));
@ -163,7 +170,7 @@ public class ListOrderedSetTest<E> extends AbstractSetTest<E> {
B b = new B();
set.add((E) a);
assertEquals(1, set.size());
set.add((E) b); // will match but not replace A as equal
set.add((E) b); // will match but not replace A as equal
assertEquals(1, set.size());
assertSame(a, set.decorated().iterator().next());
assertSame(a, set.iterator().next());
@ -171,11 +178,61 @@ public class ListOrderedSetTest<E> extends AbstractSetTest<E> {
assertSame(a, set.asList().get(0));
}
@SuppressWarnings("unchecked")
public void testRetainAll() {
List<E> list = new ArrayList<E>(10);
Set<E> set = new HashSet<E>(10);
ListOrderedSet<E> orderedSet = ListOrderedSet.listOrderedSet(set, list);
for (int i = 0; i < 10; ++i) {
orderedSet.add((E) Integer.valueOf(10 - i - 1));
}
Collection<E> retained = new ArrayList<E>(5);
for (int i = 0; i < 5; ++i) {
retained.add((E) Integer.valueOf(i * 2));
}
assertTrue(orderedSet.retainAll(retained));
assertEquals(5, orderedSet.size());
// insertion order preserved?
assertEquals(Integer.valueOf(8), orderedSet.get(0));
assertEquals(Integer.valueOf(6), orderedSet.get(1));
assertEquals(Integer.valueOf(4), orderedSet.get(2));
assertEquals(Integer.valueOf(2), orderedSet.get(3));
assertEquals(Integer.valueOf(0), orderedSet.get(4));
}
/*
* test case for https://issues.apache.org/jira/browse/COLLECTIONS-426
*/
public void testRetainAllCollections426() {
int size = 100000;
ListOrderedSet<Integer> set = new ListOrderedSet<Integer>();
for (int i = 0; i < size; i++) {
set.add(i);
}
ArrayList<Integer> list = new ArrayList<Integer>();
for (int i = size; i < 2 * size; i++) {
list.add(i);
}
long start = System.currentTimeMillis();
set.retainAll(list);
long stop = System.currentTimeMillis();
// make sure retainAll completes under 5 seconds
// TODO if test is migrated to JUnit 4, add a Timeout rule.
// http://kentbeck.github.com/junit/javadoc/latest/org/junit/rules/Timeout.html
assertTrue((stop - start) < 5000);
}
static class A {
@Override
public boolean equals(Object obj) {
return (obj instanceof A || obj instanceof B);
}
@Override
public int hashCode() {
return 1;
@ -183,10 +240,12 @@ public class ListOrderedSetTest<E> extends AbstractSetTest<E> {
}
static class B {
@Override
public boolean equals(Object obj) {
return (obj instanceof A || obj instanceof B);
}
@Override
public int hashCode() {
return 1;
@ -197,23 +256,28 @@ public class ListOrderedSetTest<E> extends AbstractSetTest<E> {
try {
ListOrderedSet.listOrderedSet((List<E>) null);
fail();
} catch (IllegalArgumentException ex) {}
} catch (IllegalArgumentException ex) {
}
try {
ListOrderedSet.listOrderedSet((Set<E>) null);
fail();
} catch (IllegalArgumentException ex) {}
} catch (IllegalArgumentException ex) {
}
try {
ListOrderedSet.listOrderedSet(null, null);
fail();
} catch (IllegalArgumentException ex) {}
} catch (IllegalArgumentException ex) {
}
try {
ListOrderedSet.listOrderedSet(new HashSet<E>(), null);
fail();
} catch (IllegalArgumentException ex) {}
} catch (IllegalArgumentException ex) {
}
try {
ListOrderedSet.listOrderedSet(null, new ArrayList<E>());
fail();
} catch (IllegalArgumentException ex) {}
} catch (IllegalArgumentException ex) {
}
}
@Override
@ -221,11 +285,11 @@ public class ListOrderedSetTest<E> extends AbstractSetTest<E> {
return "3.1";
}
// public void testCreate() throws Exception {
// resetEmpty();
// writeExternalFormToDisk((java.io.Serializable) collection, "D:/dev/collections/data/test/ListOrderedSet.emptyCollection.version3.1.obj");
// resetFull();
// writeExternalFormToDisk((java.io.Serializable) collection, "D:/dev/collections/data/test/ListOrderedSet.fullCollection.version3.1.obj");
// }
// public void testCreate() throws Exception {
// resetEmpty();
// writeExternalFormToDisk((java.io.Serializable) collection, "D:/dev/collections/data/test/ListOrderedSet.emptyCollection.version3.1.obj");
// resetFull();
// writeExternalFormToDisk((java.io.Serializable) collection, "D:/dev/collections/data/test/ListOrderedSet.fullCollection.version3.1.obj");
// }
}