diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index f9037565f..560728084 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -298,7 +298,7 @@
Calling "CollectionUtils#sizeIsEmpty(null)" will now return true.
- Added methods "CollectionUtils#merge(...)" to merge two sorted Collections
+ Added methods "CollectionUtils#collate(...)" to merge two sorted Collections
into a sorted List using the standard O(n) merge algorithm.
diff --git a/src/main/java/org/apache/commons/collections4/CollectionUtils.java b/src/main/java/org/apache/commons/collections4/CollectionUtils.java
index 122c898c1..c7520b633 100644
--- a/src/main/java/org/apache/commons/collections4/CollectionUtils.java
+++ b/src/main/java/org/apache/commons/collections4/CollectionUtils.java
@@ -38,6 +38,7 @@ import org.apache.commons.collections4.collection.UnmodifiableBoundedCollection;
import org.apache.commons.collections4.collection.UnmodifiableCollection;
import org.apache.commons.collections4.functors.Equator;
import org.apache.commons.collections4.functors.TruePredicate;
+import org.apache.commons.collections4.iterators.CollatingIterator;
import org.apache.commons.collections4.iterators.PermutationIterator;
/**
@@ -1498,9 +1499,9 @@ public class CollectionUtils {
* @throws IllegalArgumentException if either collection is null
* @since 4.0
*/
- public static > List merge(Collection extends O> a,
- Collection extends O> b) {
- return merge(a, b, ComparatorUtils.naturalComparator(), true);
+ public static > List collate(Collection extends O> a,
+ Collection extends O> b) {
+ return collate(a, b, ComparatorUtils.naturalComparator(), true);
}
/**
@@ -1519,10 +1520,10 @@ public class CollectionUtils {
* @throws IllegalArgumentException if either collection is null
* @since 4.0
*/
- public static > List merge(final Collection extends O> a,
- final Collection extends O> b,
- final boolean includeDuplicates) {
- return merge(a, b, ComparatorUtils.naturalComparator(), includeDuplicates);
+ public static > List collate(final Collection extends O> a,
+ final Collection extends O> b,
+ final boolean includeDuplicates) {
+ return collate(a, b, ComparatorUtils.naturalComparator(), includeDuplicates);
}
/**
@@ -1539,9 +1540,9 @@ public class CollectionUtils {
* @throws IllegalArgumentException if either collection or the comparator is null
* @since 4.0
*/
- public static List merge(final Collection extends O> a, final Collection extends O> b,
- final Comparator super O> c) {
- return merge(a, b, c, true);
+ public static List collate(final Collection extends O> a, final Collection extends O> b,
+ final Comparator super O> c) {
+ return collate(a, b, c, true);
}
/**
@@ -1560,8 +1561,8 @@ public class CollectionUtils {
* @throws IllegalArgumentException if either collection or the comparator is null
* @since 4.0
*/
- public static List merge(final Collection extends O> a, final Collection extends O> b,
- final Comparator super O> c, final boolean includeDuplicates) {
+ public static List collate(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");
@@ -1570,68 +1571,24 @@ public class CollectionUtils {
throw new IllegalArgumentException("The comparator must not be null");
}
- final List mergedList = new ArrayList(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
- * @since 4.0
- */
- private static E addItemToList(final E item, final List list, final E lastItem,
- final boolean includeDuplicates) {
- if (includeDuplicates || lastItem == null || !lastItem.equals(item)) {
- list.add(item);
- return item;
+ final int totalSize = Math.max(1, a.size() + b.size());
+ final Iterator iterator = new CollatingIterator(c, a.iterator(), b.iterator());
+ if (includeDuplicates) {
+ return IteratorUtils.toList(iterator, totalSize);
} else {
- return lastItem;
+ final ArrayList mergedList = new ArrayList(totalSize);
+
+ O lastItem = null;
+ while (iterator.hasNext()) {
+ final O item = iterator.next();
+ if (lastItem == null || !lastItem.equals(item)) {
+ mergedList.add(item);
+ }
+ lastItem = item;
+ }
+
+ mergedList.trimToSize();
+ return mergedList;
}
}
diff --git a/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java b/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java
index 0945ab22f..7c01bbf4f 100644
--- a/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java
+++ b/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java
@@ -1640,25 +1640,25 @@ public class CollectionUtilsTest extends MockTestCase {
}
@Test(expected=IllegalArgumentException.class)
- public void mergeException1() {
- CollectionUtils.merge(collectionA, null);
+ public void collateException1() {
+ CollectionUtils.collate(collectionA, null);
}
@Test(expected=IllegalArgumentException.class)
- public void mergeException2() {
- CollectionUtils.merge(collectionA, collectionC, null);
+ public void collateException2() {
+ CollectionUtils.collate(collectionA, collectionC, null);
}
@Test
- public void testMerge() {
- List result = CollectionUtils.merge(emptyCollection, emptyCollection);
+ public void testCollate() {
+ List result = CollectionUtils.collate(emptyCollection, emptyCollection);
assertEquals("Merge empty with empty", 0, result.size());
- result = CollectionUtils.merge(collectionA, emptyCollection);
+ result = CollectionUtils.collate(collectionA, emptyCollection);
assertEquals("Merge empty with non-empty", collectionA, result);
- List result1 = CollectionUtils.merge(collectionD, collectionE);
- List result2 = CollectionUtils.merge(collectionE, collectionD);
+ List result1 = CollectionUtils.collate(collectionD, collectionE);
+ List result2 = CollectionUtils.collate(collectionE, collectionD);
assertEquals("Merge two lists 1", result1, result2);
List combinedList = new ArrayList();
@@ -1671,23 +1671,23 @@ public class CollectionUtilsTest extends MockTestCase {
final Comparator reverseComparator =
ComparatorUtils.reversedComparator(ComparatorUtils.naturalComparator());
- result = CollectionUtils.merge(emptyCollection, emptyCollection, reverseComparator);
+ result = CollectionUtils.collate(emptyCollection, emptyCollection, reverseComparator);
assertEquals("Comparator Merge empty with empty", 0, result.size());
Collections.reverse((List) collectionD);
Collections.reverse((List) collectionE);
Collections.reverse(combinedList);
- result1 = CollectionUtils.merge(collectionD, collectionE, reverseComparator);
- result2 = CollectionUtils.merge(collectionE, collectionD, reverseComparator);
+ result1 = CollectionUtils.collate(collectionD, collectionE, reverseComparator);
+ result2 = CollectionUtils.collate(collectionE, collectionD, reverseComparator);
assertEquals("Comparator Merge two lists 1", result1, result2);
assertEquals("Comparator Merge two lists 2", combinedList, result2);
}
@Test
- public void testMergeIgnoreDuplicates() {
- List result1 = CollectionUtils.merge(collectionD, collectionE, false);
- List result2 = CollectionUtils.merge(collectionE, collectionD, false);
+ public void testCollateIgnoreDuplicates() {
+ List result1 = CollectionUtils.collate(collectionD, collectionE, false);
+ List result2 = CollectionUtils.collate(collectionE, collectionD, false);
assertEquals("Merge two lists 1 - ignore duplicates", result1, result2);
Set combinedSet = new HashSet();