mirror of
https://github.com/apache/commons-collections.git
synced 2025-03-01 14:29:06 +00:00
[COLLECTIONS-296] Added CollectionUtils#merge methods to merge two sorted Collections. Thanks to Julius Davies for the report and patch.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1469577 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
fd5c46d080
commit
5ea7e3a7d4
@ -22,6 +22,10 @@
|
|||||||
<body>
|
<body>
|
||||||
|
|
||||||
<release version="4.0" date="TBA" description="Next release">
|
<release version="4.0" date="TBA" description="Next release">
|
||||||
|
<action issue="COLLECTIONS-296" dev="tn" type="add" due-to="Julius Davies">
|
||||||
|
Added methods "CollectionUtils#merge(...)" to merge two sorted Collections
|
||||||
|
into a sorted List using the standard O(n) merge algorithm.
|
||||||
|
</action>
|
||||||
<action issue="COLLECTIONS-429,COLLECTION-434" dev="tn" type="add" due-to="Adrian Nistor, Mert Guldur">
|
<action issue="COLLECTIONS-429,COLLECTION-434" dev="tn" type="add" due-to="Adrian Nistor, Mert Guldur">
|
||||||
Added method "CollectionUtils#containsAll(Collection, Collection)" with guaranteed
|
Added method "CollectionUtils#containsAll(Collection, Collection)" with guaranteed
|
||||||
runtime complexity of O(n + m) and space complexity of O(n). This method may yield much
|
runtime complexity of O(n + m) and space complexity of O(n). This method may yield much
|
||||||
|
@ -19,6 +19,7 @@ package org.apache.commons.collections4;
|
|||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -1438,6 +1439,152 @@ public class CollectionUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Merges two sorted Collections, a and b, into a single, sorted List
|
||||||
|
* such that the natural ordering of the elements is retained.
|
||||||
|
* <p>
|
||||||
|
* Uses the standard O(n) merge algorithm for combining two sorted lists.
|
||||||
|
*
|
||||||
|
* @param <O> the element type
|
||||||
|
* @param a the first collection, must not be null
|
||||||
|
* @param b the second collection, must not be null
|
||||||
|
* @return a new sorted List, containing the elements of Collection a and b
|
||||||
|
* @throws IllegalArgumentException if either collection is null
|
||||||
|
*/
|
||||||
|
public static <O extends Comparable<? super O>> List<O> merge(Collection<? extends O> a,
|
||||||
|
Collection<? extends O> b) {
|
||||||
|
return merge(a, b, ComparatorUtils.<O>naturalComparator(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
/**
|
||||||
|
* Merges two sorted Collections, a and b, into a single, sorted List
|
||||||
|
* such that the natural ordering of the elements is retained.
|
||||||
|
* <p>
|
||||||
|
* Uses the standard O(n) merge algorithm for combining two sorted lists.
|
||||||
|
*
|
||||||
|
* @param <O> the element type
|
||||||
|
* @param a the first collection, must not be null
|
||||||
|
* @param b the second collection, must not be null
|
||||||
|
* @param includeDuplicates if {@code true} duplicate elements will be retained, otherwise
|
||||||
|
* they will be removed in the output collection
|
||||||
|
* @return a new sorted List, containing the elements of Collection a and b
|
||||||
|
* @throws IllegalArgumentException if either collection is null
|
||||||
|
*/
|
||||||
|
public static <O extends Comparable<? super O>> List<O> merge(final Collection<? extends O> a,
|
||||||
|
final Collection<? extends O> b,
|
||||||
|
final boolean includeDuplicates) {
|
||||||
|
return merge(a, b, ComparatorUtils.<O>naturalComparator(), includeDuplicates);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges two sorted Collections, a and b, into a single, sorted List
|
||||||
|
* such that the ordering of the elements according to Comparator c is retained.
|
||||||
|
* <p>
|
||||||
|
* Uses the standard O(n) merge algorithm for combining two sorted lists.
|
||||||
|
*
|
||||||
|
* @param <O> the element type
|
||||||
|
* @param a the first collection, must not be null
|
||||||
|
* @param b the second collection, must not be null
|
||||||
|
* @param c the comparator to use for the merge.
|
||||||
|
* @return a new sorted List, containing the elements of Collection a and b
|
||||||
|
* @throws IllegalArgumentException if either collection or the comparator is null
|
||||||
|
*/
|
||||||
|
public static <O> List<O> merge(final Collection<? extends O> a, final Collection<? extends O> b,
|
||||||
|
final Comparator<? super O> c) {
|
||||||
|
return merge(a, b, c, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges two sorted Collections, a and b, into a single, sorted List
|
||||||
|
* such that the ordering of the elements according to Comparator c is retained.
|
||||||
|
* <p>
|
||||||
|
* Uses the standard O(n) merge algorithm for combining two sorted lists.
|
||||||
|
*
|
||||||
|
* @param <O> the element type
|
||||||
|
* @param a the first collection, must not be null
|
||||||
|
* @param b the second collection, must not be null
|
||||||
|
* @param c the comparator to use for the merge.
|
||||||
|
* @param includeDuplicates if {@code true} duplicate elements will be retained, otherwise
|
||||||
|
* they will be removed in the output collection
|
||||||
|
* @return a new sorted List, containing the elements of Collection a and b
|
||||||
|
* @throws IllegalArgumentException if either collection or the comparator is null
|
||||||
|
*/
|
||||||
|
public static <O> List<O> merge(final Collection<? extends O> a, final Collection<? extends O> b,
|
||||||
|
final Comparator<? super O> c, final boolean includeDuplicates) {
|
||||||
|
|
||||||
|
if (a == null || b == null) {
|
||||||
|
throw new IllegalArgumentException("The collections must not be null");
|
||||||
|
}
|
||||||
|
if (c == null) {
|
||||||
|
throw new IllegalArgumentException("The comparator must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<O> mergedList = new ArrayList<O>(a.size() + b.size());
|
||||||
|
|
||||||
|
// if either collection is empty, just return the other one
|
||||||
|
if (a.isEmpty() || b.isEmpty()) {
|
||||||
|
mergedList.addAll(a.isEmpty() ? b : a);
|
||||||
|
return mergedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Iterator<? extends O> it1 = a.iterator();
|
||||||
|
final Iterator<? extends O> it2 = b.iterator();
|
||||||
|
O o1 = it1.next();
|
||||||
|
O o2 = it2.next();
|
||||||
|
O last = null;
|
||||||
|
while (true) {
|
||||||
|
final int x = c.compare(o1, o2);
|
||||||
|
if (x <= 0) {
|
||||||
|
last = addItemToList(o1, mergedList, last, includeDuplicates);
|
||||||
|
if (it1.hasNext()) {
|
||||||
|
o1 = it1.next();
|
||||||
|
} else {
|
||||||
|
// a is empty, so add current element of b
|
||||||
|
last = addItemToList(o2, mergedList, last, includeDuplicates);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
last = addItemToList(o2, mergedList, last, includeDuplicates);
|
||||||
|
if (it2.hasNext()) {
|
||||||
|
o2 = it2.next();
|
||||||
|
} else {
|
||||||
|
// b is empty, so add current element of a
|
||||||
|
last = addItemToList(o1, mergedList, last, includeDuplicates);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the remaining elements from the non-empty iterator
|
||||||
|
final Iterator<? extends O> it = it1.hasNext() ? it1 : it2;
|
||||||
|
while (it.hasNext()) {
|
||||||
|
last = addItemToList(it.next(), mergedList, last, includeDuplicates);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an item to the specified list.
|
||||||
|
*
|
||||||
|
* @param item the item to add
|
||||||
|
* @param list the list to use
|
||||||
|
* @param lastItem the last added item, may be null
|
||||||
|
* @param includeDuplicates whether duplicate entries are allowed
|
||||||
|
* @return the last added item
|
||||||
|
*/
|
||||||
|
private static <E> E addItemToList(final E item, final List<E> list, final E lastItem,
|
||||||
|
final boolean includeDuplicates) {
|
||||||
|
if (includeDuplicates || lastItem == null || !lastItem.equals(item)) {
|
||||||
|
list.add(item);
|
||||||
|
return item;
|
||||||
|
} else {
|
||||||
|
return lastItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* Returns a collection containing all the elements in <code>collection</code>
|
* Returns a collection containing all the elements in <code>collection</code>
|
||||||
|
@ -70,6 +70,16 @@ public class CollectionUtilsTest extends MockTestCase {
|
|||||||
*/
|
*/
|
||||||
private Collection<Integer> collectionC = null;
|
private Collection<Integer> collectionC = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorted Collection of {@link Integer}s
|
||||||
|
*/
|
||||||
|
private Collection<Integer> collectionD = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorted Collection of {@link Integer}s
|
||||||
|
*/
|
||||||
|
private Collection<Integer> collectionE = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collection of {@link Integer}s, bound as {@link Number}s
|
* Collection of {@link Integer}s, bound as {@link Number}s
|
||||||
*/
|
*/
|
||||||
@ -96,6 +106,8 @@ public class CollectionUtilsTest extends MockTestCase {
|
|||||||
|
|
||||||
private Iterable<Number> iterableB2 = null;
|
private Iterable<Number> iterableB2 = null;
|
||||||
|
|
||||||
|
private Collection<Integer> emptyCollection = new ArrayList<Integer>(1);
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
collectionA = new ArrayList<Integer>();
|
collectionA = new ArrayList<Integer>();
|
||||||
@ -134,6 +146,25 @@ public class CollectionUtilsTest extends MockTestCase {
|
|||||||
collectionC2 = new LinkedList<Number>(collectionC);
|
collectionC2 = new LinkedList<Number>(collectionC);
|
||||||
iterableA2 = collectionA2;
|
iterableA2 = collectionA2;
|
||||||
iterableB2 = collectionB2;
|
iterableB2 = collectionB2;
|
||||||
|
|
||||||
|
collectionD = new ArrayList<Integer>();
|
||||||
|
collectionD.add(1);
|
||||||
|
collectionD.add(3);
|
||||||
|
collectionD.add(3);
|
||||||
|
collectionD.add(3);
|
||||||
|
collectionD.add(5);
|
||||||
|
collectionD.add(7);
|
||||||
|
collectionD.add(7);
|
||||||
|
collectionD.add(10);
|
||||||
|
|
||||||
|
collectionE = new ArrayList<Integer>();
|
||||||
|
collectionE.add(2);
|
||||||
|
collectionE.add(4);
|
||||||
|
collectionE.add(4);
|
||||||
|
collectionE.add(5);
|
||||||
|
collectionE.add(6);
|
||||||
|
collectionE.add(6);
|
||||||
|
collectionE.add(9);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -1553,4 +1584,65 @@ public class CollectionUtilsTest extends MockTestCase {
|
|||||||
expect(iterator.hasNext()).andReturn(true);
|
expect(iterator.hasNext()).andReturn(true);
|
||||||
expect(iterator.next()).andReturn(t);
|
expect(iterator.next()).andReturn(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected=IllegalArgumentException.class)
|
||||||
|
public void mergeException1() {
|
||||||
|
CollectionUtils.merge(collectionA, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=IllegalArgumentException.class)
|
||||||
|
public void mergeException2() {
|
||||||
|
CollectionUtils.merge(collectionA, collectionC, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMerge() {
|
||||||
|
List<Integer> result = CollectionUtils.merge(emptyCollection, emptyCollection);
|
||||||
|
assertEquals("Merge empty with empty", 0, result.size());
|
||||||
|
|
||||||
|
result = CollectionUtils.merge(collectionA, emptyCollection);
|
||||||
|
assertEquals("Merge empty with non-empty", collectionA, result);
|
||||||
|
|
||||||
|
List<Integer> result1 = CollectionUtils.merge(collectionD, collectionE);
|
||||||
|
List<Integer> result2 = CollectionUtils.merge(collectionE, collectionD);
|
||||||
|
assertEquals("Merge two lists 1", result1, result2);
|
||||||
|
|
||||||
|
List<Integer> combinedList = new ArrayList<Integer>();
|
||||||
|
combinedList.addAll(collectionD);
|
||||||
|
combinedList.addAll(collectionE);
|
||||||
|
Collections.sort(combinedList);
|
||||||
|
|
||||||
|
assertEquals("Merge two lists 2", combinedList, result2);
|
||||||
|
|
||||||
|
final Comparator<Integer> reverseComparator =
|
||||||
|
ComparatorUtils.reversedComparator(ComparatorUtils.<Integer>naturalComparator());
|
||||||
|
|
||||||
|
result = CollectionUtils.merge(emptyCollection, emptyCollection, reverseComparator);
|
||||||
|
assertEquals("Comparator Merge empty with empty", 0, result.size());
|
||||||
|
|
||||||
|
Collections.reverse((List<Integer>) collectionD);
|
||||||
|
Collections.reverse((List<Integer>) collectionE);
|
||||||
|
Collections.reverse(combinedList);
|
||||||
|
|
||||||
|
result1 = CollectionUtils.merge(collectionD, collectionE, reverseComparator);
|
||||||
|
result2 = CollectionUtils.merge(collectionE, collectionD, reverseComparator);
|
||||||
|
assertEquals("Comparator Merge two lists 1", result1, result2);
|
||||||
|
assertEquals("Comparator Merge two lists 2", combinedList, result2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMergeIgnoreDuplicates() {
|
||||||
|
List<Integer> result1 = CollectionUtils.merge(collectionD, collectionE, false);
|
||||||
|
List<Integer> result2 = CollectionUtils.merge(collectionE, collectionD, false);
|
||||||
|
assertEquals("Merge two lists 1 - ignore duplicates", result1, result2);
|
||||||
|
|
||||||
|
Set<Integer> combinedSet = new HashSet<Integer>();
|
||||||
|
combinedSet.addAll(collectionD);
|
||||||
|
combinedSet.addAll(collectionE);
|
||||||
|
List<Integer> combinedList = new ArrayList<Integer>(combinedSet);
|
||||||
|
Collections.sort(combinedList);
|
||||||
|
|
||||||
|
assertEquals("Merge two lists 2 - ignore duplicates", combinedList, result2);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user