From ee04f21f29a80e17b03bbe84d140d0545d1ce4ef Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 20 Nov 2018 19:53:33 +0100 Subject: [PATCH] removeAll is now O(n+m) if both lists are sorted --- .../java/org/lucares/collections/IntList.java | 36 ++++++++++++++++--- .../org/lucares/collections/LongList.java | 35 +++++++++++++++--- .../org/lucares/collections/IntListTest.java | 22 ++++++++++++ .../org/lucares/collections/LongListTest.java | 22 ++++++++++++ 4 files changed, 105 insertions(+), 10 deletions(-) diff --git a/primitiveCollections/src/main/java/org/lucares/collections/IntList.java b/primitiveCollections/src/main/java/org/lucares/collections/IntList.java index 3c00b83..1bf3a6a 100644 --- a/primitiveCollections/src/main/java/org/lucares/collections/IntList.java +++ b/primitiveCollections/src/main/java/org/lucares/collections/IntList.java @@ -17,6 +17,7 @@ import java.util.stream.StreamSupport; */ public final class IntList implements Serializable, Cloneable { + // TODO retainAll on sorted lists // TODO support Iterator // TODO add mod counts // TODO support sublists @@ -433,17 +434,42 @@ public final class IntList implements Serializable, Cloneable { * This method does not release any memory. Call {@link #trim()} to free unused * memory. *

- * If {@code retain} is sorted, then the algorithm has a complexity of - * O(n*log(m)), where n is the length of {@code this} and m the length of - * {@code retain}. If {@code retain} is not sorted, then the complexity is - * O(n*m). + * If both lists are sorted, then the algorithms has time complexity of O(n+m), + * where n is the length of {@code this} and m the length of {@code remove}. If + * only {@code remove} is sorted, then the algorithm has a complexity of + * O(n*log(m)). If {@code remove} is not sorted, then the complexity is O(n*m). + * The space complexity is constant in all cases. * * @param remove the elements to remove * @throws NullPointerException if the specified {@link IntList} is null * @see #trim() */ public void removeAll(final IntList remove) { - removeIf((val, index) -> remove.indexOf(val) >= 0); + + if (isSorted() && remove.isSorted()) { + removeSorted(remove); + } else { + removeIf((val, index) -> remove.indexOf(val) >= 0); + } + } + + private void removeSorted(final IntList remove) { + int posInRemoveList = 0; + int insertPosition = 0; + for (int i = 0; i < size; i++) { + final int current = data[i]; + + while (posInRemoveList < remove.size() && remove.get(posInRemoveList) < current) { + posInRemoveList++; + } + + if (posInRemoveList >= remove.size() || remove.get(posInRemoveList) != current) { + // keep current element + data[insertPosition] = current; + insertPosition++; + } + } + size = insertPosition; } /** diff --git a/primitiveCollections/src/main/java/org/lucares/collections/LongList.java b/primitiveCollections/src/main/java/org/lucares/collections/LongList.java index 1378f89..95d328c 100644 --- a/primitiveCollections/src/main/java/org/lucares/collections/LongList.java +++ b/primitiveCollections/src/main/java/org/lucares/collections/LongList.java @@ -423,17 +423,42 @@ public final class LongList implements Serializable, Cloneable { * This method does not release any memory. Call {@link #trim()} to free unused * memory. *

- * If {@code retain} is sorted, then the algorithm has a complexity of - * O(n*log(m)), where n is the length of {@code this} and m the length of - * {@code retain}. If {@code retain} is not sorted, then the complexity is - * O(n*m). + * If both lists are sorted, then the algorithms has time complexity of O(n+m), + * where n is the length of {@code this} and m the length of {@code remove}. If + * only {@code remove} is sorted, then the algorithm has a complexity of + * O(n*log(m)). If {@code remove} is not sorted, then the complexity is O(n*m). + * The space complexity is constant in all cases. * * @param remove the elements to remove * @throws NullPointerException if the specified {@link LongList} is null * @see #trim() */ public void removeAll(final LongList remove) { - removeIf((val, index) -> remove.indexOf(val) >= 0); + + if (isSorted() && remove.isSorted()) { + removeSorted(remove); + } else { + removeIf((val, index) -> remove.indexOf(val) >= 0); + } + } + + private void removeSorted(final LongList remove) { + int posInRemoveList = 0; + int insertPosition = 0; + for (int i = 0; i < size; i++) { + final long current = data[i]; + + while (posInRemoveList < remove.size() && remove.get(posInRemoveList) < current) { + posInRemoveList++; + } + + if (posInRemoveList >= remove.size() || remove.get(posInRemoveList) != current) { + // keep current element + data[insertPosition] = current; + insertPosition++; + } + } + size = insertPosition; } /** diff --git a/primitiveCollections/src/test/java/org/lucares/collections/IntListTest.java b/primitiveCollections/src/test/java/org/lucares/collections/IntListTest.java index 56eea9c..57fc3fa 100644 --- a/primitiveCollections/src/test/java/org/lucares/collections/IntListTest.java +++ b/primitiveCollections/src/test/java/org/lucares/collections/IntListTest.java @@ -1114,6 +1114,28 @@ public class IntListTest { Assertions.assertEquals(0, list.size()); } + @Test + public void testRemoveAllFromSortedList() { + final IntList list = IntList.of(1, 2, 3, 4, 5); + final IntList remove = IntList.of(1, 3); + + list.removeAll(remove); + Assertions.assertArrayEquals(new int[] { 2, 4, 5 }, list.toArray()); + Assertions.assertEquals(3, list.size()); + Assertions.assertTrue(list.isSorted()); + } + + @Test + public void testRemoveAllFromSortedList2() { + final IntList list = IntList.of(2, 4, 4, 6, 8); + final IntList remove = IntList.of(1, 4, 9); + + list.removeAll(remove); + Assertions.assertArrayEquals(new int[] { 2, 6, 8 }, list.toArray()); + Assertions.assertEquals(3, list.size()); + Assertions.assertTrue(list.isSorted()); + } + @Test public void testRetainAll() { final IntList list = IntList.of(-2, -1, 0, 1, 2, 3, 4, 5, 6); diff --git a/primitiveCollections/src/test/java/org/lucares/collections/LongListTest.java b/primitiveCollections/src/test/java/org/lucares/collections/LongListTest.java index eca9207..978464a 100644 --- a/primitiveCollections/src/test/java/org/lucares/collections/LongListTest.java +++ b/primitiveCollections/src/test/java/org/lucares/collections/LongListTest.java @@ -1104,6 +1104,28 @@ public class LongListTest { Assertions.assertEquals(0, list.size()); } + @Test + public void testRemoveAllFromSortedList() { + final LongList list = LongList.of(1, 2, 3, 4, 5); + final LongList remove = LongList.of(1, 3); + + list.removeAll(remove); + Assertions.assertArrayEquals(new long[] { 2, 4, 5 }, list.toArray()); + Assertions.assertEquals(3, list.size()); + Assertions.assertTrue(list.isSorted()); + } + + @Test + public void testRemoveAllFromSortedList2() { + final LongList list = LongList.of(2, 4, 4, 6, 8); + final LongList remove = LongList.of(1, 4, 9); + + list.removeAll(remove); + Assertions.assertArrayEquals(new long[] { 2, 6, 8 }, list.toArray()); + Assertions.assertEquals(3, list.size()); + Assertions.assertTrue(list.isSorted()); + } + @Test public void testRetainAll() { final LongList list = LongList.of(-2, -1, 0, 1, 2, 3, 4, 5, 6);