This is an automated email from the ASF dual-hosted git repository. daim pushed a commit to branch OAK-11543 in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
commit 201a7cb4134fe13334f3f49252bafe2fe960c560 Author: Rishabh Kumar <[email protected]> AuthorDate: Tue Mar 4 10:07:26 2025 +0530 OAK-11543 : replaced Guava's Iterables.elementEquals with oak-commons util --- .../oak/commons/collections/IterableUtils.java | 38 +++++++ .../oak/commons/collections/IteratorUtils.java | 33 ++++++ .../oak/commons/collections/IterableUtilsTest.java | 126 +++++++++++++++++++++ .../oak/commons/collections/IteratorUtilsTest.java | 88 ++++++++++++++ 4 files changed, 285 insertions(+) diff --git a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IterableUtils.java b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IterableUtils.java index 52cc0a36f6..4283983b08 100644 --- a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IterableUtils.java +++ b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IterableUtils.java @@ -18,6 +18,7 @@ */ package org.apache.jackrabbit.oak.commons.collections; +import com.google.common.collect.Iterators; import org.apache.commons.collections4.Predicate; import org.apache.commons.collections4.iterators.LazyIteratorChain; import org.apache.jackrabbit.oak.commons.conditions.Validate; @@ -320,4 +321,41 @@ public class IterableUtils { final Iterable<T> iterable = () -> IteratorUtils.mergeSorted(org.apache.commons.collections4.IterableUtils.transformedIterable(iterables, Iterable::iterator), c); return org.apache.commons.collections4.IterableUtils.unmodifiableIterable(iterable); } + + /** + * Checks if two iterables have the same elements in the same order. + * <p> + * This method iterates through both iterables and compares each corresponding pair of elements using + * {@link Objects#equals(Object, Object)}. + * <p> + * Note that both iterables will be fully traversed during the comparison. + * + * @param itr1 the first iterable to compare, may be null + * @param itr2 the second iterable to compare, may be null + * @return {@code true} if both iterables contain the same elements in the same order, {@code false} otherwise. + * Returns {@code true} if both iterables are null and {@code false} if only one is null. + * + * @see IteratorUtils#elementsEqual(Iterator, Iterator) + */ + public static boolean elementsEqual(final Iterable<?> itr1, final Iterable<?> itr2) { + + if (itr1 == itr2) { + // Both are null or the same instance + return true; + } + + if (itr1 == null || itr2 == null) { + // returns false if one of the iterator is null + return false; + } + + if (itr1 instanceof Collection && itr2 instanceof Collection) { + Collection<?> c1 = (Collection<?>) itr1; + Collection<?> c2 = (Collection<?>) itr2; + if (c1.size() != c2.size()) { + return false; + } + } + return IteratorUtils.elementsEqual(itr1.iterator(), itr2.iterator()); + } } diff --git a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IteratorUtils.java b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IteratorUtils.java index c392724501..1da3dfa3b1 100644 --- a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IteratorUtils.java +++ b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/collections/IteratorUtils.java @@ -144,4 +144,37 @@ public class IteratorUtils { return next; } } + + /** + * Compares two iterators to determine if they contain the same elements in the same order. + * <p> + * This method iterates through both iterators and compares each corresponding pair of elements. + * <p> + * Note that this method consumes both iterators. + * + * @param iterator1 the first iterator to compare, may be null + * @param iterator2 the second iterator to compare, may be null + * @return {@code true} if both iterators contain the same number of elements and all corresponding elements + * are equal, {@code false} otherwise. + */ + public static boolean elementsEqual(final Iterator<?> iterator1, final Iterator<?> iterator2) { + if (iterator1 == iterator2) { + // returns true if both point to same object or both are null + return true; + } + + if (iterator1 == null || iterator2 == null) { + // returns false if one of the iterator is null + return false; + } + + while (iterator1.hasNext() && iterator2.hasNext()) { + if (!Objects.equals(iterator1.next(), iterator2.next())) { + return false; + } + } + + // return true if both the iterators have same number of elements + return !iterator1.hasNext() && !iterator2.hasNext(); + } } diff --git a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IterableUtilsTest.java b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IterableUtilsTest.java index 2ab596addc..9c1aa94551 100644 --- a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IterableUtilsTest.java +++ b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IterableUtilsTest.java @@ -22,10 +22,12 @@ import org.apache.commons.collections4.Predicate; import org.junit.Assert; import org.junit.Test; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; import java.util.function.Function; @@ -629,4 +631,128 @@ public class IterableUtilsTest { Assert.assertFalse(merged.iterator().hasNext()); Assert.assertThrows(NoSuchElementException.class, () -> merged.iterator().next()); } + + @Test + public void testNullIterables() { + // Both null + Assert.assertTrue(IterableUtils.elementsEqual(null, null)); + + // One null + Assert.assertFalse(IterableUtils.elementsEqual(null, Collections.emptyList())); + Assert.assertFalse(IterableUtils.elementsEqual(Collections.emptyList(), null)); + } + + @Test + public void testEmptyIterables() { + Iterable<String> empty1 = Collections.emptyList(); + Iterable<String> empty2 = Collections.emptyList(); + + Assert.assertTrue(IterableUtils.elementsEqual(empty1, empty2)); + } + + @Test + public void testSameIterable() { + Iterable<String> iterable = Arrays.asList("a", "b", "c"); + Assert.assertTrue(IterableUtils.elementsEqual(iterable, iterable)); + } + + @Test + public void testEqualIterables() { + Iterable<String> iterable1 = Arrays.asList("a", "b", "c"); + Iterable<String> iterable2 = Arrays.asList("a", "b", "c"); + + Assert.assertTrue(IterableUtils.elementsEqual(iterable1, iterable2)); + } + + @Test + public void testDifferentElements() { + Iterable<String> iterable1 = Arrays.asList("a", "b", "c"); + Iterable<String> iterable2 = Arrays.asList("a", "d", "c"); + + Assert.assertFalse(IterableUtils.elementsEqual(iterable1, iterable2)); + } + + @Test + public void testDifferentLengthsFirstLonger() { + Iterable<String> iterable1 = Arrays.asList("a", "b", "c", "d"); + Iterable<String> iterable2 = Arrays.asList("a", "b", "c"); + + Assert.assertFalse(IterableUtils.elementsEqual(iterable1, iterable2)); + } + + @Test + public void testDifferentLengthsSecondLonger() { + Iterable<String> iterable1 = Arrays.asList("a", "b"); + Iterable<String> iterable2 = Arrays.asList("a", "b", "c"); + + Assert.assertFalse(IterableUtils.elementsEqual(iterable1, iterable2)); + } + + @Test + public void testWithNullElements() { + Iterable<String> iterable1 = Arrays.asList("a", null, "c"); + Iterable<String> iterable2 = Arrays.asList("a", null, "c"); + + Assert.assertTrue(IterableUtils.elementsEqual(iterable1, iterable2)); + + Iterable<String> iterable3 = Arrays.asList("a", null, "c"); + Iterable<String> iterable4 = Arrays.asList("a", "b", "c"); + + Assert.assertFalse(IterableUtils.elementsEqual(iterable3, iterable4)); + } + + @Test + public void testCollectionSizeOptimization() { + // Different sizes should return false quickly without comparing elements + List<Integer> list1 = new ArrayList<>(); + List<Integer> list2 = new ArrayList<>(); + + for (int i = 0; i < 1000; i++) { + list1.add(i); + } + + for (int i = 0; i < 999; i++) { + list2.add(i); + } + + Assert.assertFalse(IterableUtils.elementsEqual(list1, list2)); + } + + @Test + public void testLargeIterables() { + // Create two large identical lists + List<Integer> list1 = new ArrayList<>(); + List<Integer> list2 = new ArrayList<>(); + for (int i = 0; i < 10000; i++) { + list1.add(i); + list2.add(i); + } + + Assert.assertTrue(IterableUtils.elementsEqual(list1, list2)); + + // Modify one value in the second list + list2.set(9999, -1); + Assert.assertFalse(IterableUtils.elementsEqual(list1, list2)); + } + + @Test + public void testCustomIterableImplementations() { + // Test with a custom iterable implementation + Iterable<Integer> customIterable1 = () -> Arrays.asList(1, 2, 3).iterator(); + Iterable<Integer> customIterable2 = () -> Arrays.asList(1, 2, 3).iterator(); + + Assert.assertTrue(IterableUtils.elementsEqual(customIterable1, customIterable2)); + + Iterable<Integer> customIterable3 = () -> Arrays.asList(1, 2, 4).iterator(); + Assert.assertFalse(IterableUtils.elementsEqual(customIterable1, customIterable3)); + } + + @Test + public void testMixedCollectionTypes() { + // Test with different collection implementations + List<String> arrayList = new ArrayList<>(Arrays.asList("a", "b", "c")); + List<String> linkedList = new LinkedList<>(Arrays.asList("a", "b", "c")); + + Assert.assertTrue(IterableUtils.elementsEqual(arrayList, linkedList)); + } } diff --git a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IteratorUtilsTest.java b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IteratorUtilsTest.java index 02eefee034..5986534bc2 100644 --- a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IteratorUtilsTest.java +++ b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/collections/IteratorUtilsTest.java @@ -21,7 +21,9 @@ package org.apache.jackrabbit.oak.commons.collections; import org.junit.Assert; import org.junit.Test; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; @@ -143,4 +145,90 @@ public class IteratorUtilsTest { Assert.assertThrows(NoSuchElementException.class, merged::next); } + + @Test + public void testNullIterators() { + // Both null + Assert.assertTrue(IteratorUtils.elementsEqual(null, null)); + + // One null + Assert.assertFalse(IteratorUtils.elementsEqual(null, Collections.emptyIterator())); + Assert.assertFalse(IteratorUtils.elementsEqual(Collections.emptyIterator(), null)); + } + + @Test + public void testEmptyIterators() { + Iterator<String> empty1 = Collections.emptyIterator(); + Iterator<String> empty2 = Collections.emptyIterator(); + + Assert.assertTrue(IteratorUtils.elementsEqual(empty1, empty2)); + } + + @Test + public void testSameIterator() { + Iterator<String> iterator = Arrays.asList("a", "b", "c").iterator(); + Assert.assertTrue(IteratorUtils.elementsEqual(iterator, iterator)); + } + + @Test + public void testEqualIterators() { + Iterator<String> iterator1 = Arrays.asList("a", "b", "c").iterator(); + Iterator<String> iterator2 = Arrays.asList("a", "b", "c").iterator(); + + Assert.assertTrue(IteratorUtils.elementsEqual(iterator1, iterator2)); + } + + @Test + public void testDifferentElements() { + Iterator<String> iterator1 = Arrays.asList("a", "b", "c").iterator(); + Iterator<String> iterator2 = Arrays.asList("a", "d", "c").iterator(); + + Assert.assertFalse(IteratorUtils.elementsEqual(iterator1, iterator2)); + } + + @Test + public void testDifferentLengthsFirstLonger() { + Iterator<String> iterator1 = Arrays.asList("a", "b", "c", "d").iterator(); + Iterator<String> iterator2 = Arrays.asList("a", "b", "c").iterator(); + + Assert.assertFalse(IteratorUtils.elementsEqual(iterator1, iterator2)); + } + + @Test + public void testDifferentLengthsSecondLonger() { + Iterator<String> iterator1 = Arrays.asList("a", "b").iterator(); + Iterator<String> iterator2 = Arrays.asList("a", "b", "c").iterator(); + + Assert.assertFalse(IteratorUtils.elementsEqual(iterator1, iterator2)); + } + + @Test + public void testWithNullElements() { + Iterator<String> iterator1 = Arrays.asList("a", null, "c").iterator(); + Iterator<String> iterator2 = Arrays.asList("a", null, "c").iterator(); + + Assert.assertTrue(IteratorUtils.elementsEqual(iterator1, iterator2)); + + Iterator<String> iterator3 = Arrays.asList("a", null, "c").iterator(); + Iterator<String> iterator4 = Arrays.asList("a", "b", "c").iterator(); + + Assert.assertFalse(IteratorUtils.elementsEqual(iterator3, iterator4)); + } + + @Test + public void testLargeIterators() { + // Create two large identical lists + List<Integer> list1 = new ArrayList<>(); + List<Integer> list2 = new ArrayList<>(); + for (int i = 0; i < 10000; i++) { + list1.add(i); + list2.add(i); + } + + Assert.assertTrue(IteratorUtils.elementsEqual(list1.iterator(), list2.iterator())); + + // Modify one value in the second list + list2.set(9999, -1); + Assert.assertFalse(IteratorUtils.elementsEqual(list1.iterator(), list2.iterator())); + } }
