From fef9af51cdf770abcc40bf6fce17c6e0026293d9 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Sun, 23 Sep 2018 15:28:37 +0200 Subject: [PATCH] add different versions of IntList.insert and LongList.insert The new methods are more flexible, so that you can insert parts of an array. --- .../java/org/lucares/collections/IntList.java | 115 +++++++++++++---- .../org/lucares/collections/LongList.java | 117 ++++++++++++++---- .../org/lucares/collections/TriValueBool.java | 8 ++ .../org/lucares/collections/IntListTest.java | 103 ++++++++++++++- .../org/lucares/collections/LongListTest.java | 87 ++++++++++++- 5 files changed, 376 insertions(+), 54 deletions(-) create mode 100644 primitiveCollections/src/main/java/org/lucares/collections/TriValueBool.java diff --git a/primitiveCollections/src/main/java/org/lucares/collections/IntList.java b/primitiveCollections/src/main/java/org/lucares/collections/IntList.java index 39597aa..3c00b83 100644 --- a/primitiveCollections/src/main/java/org/lucares/collections/IntList.java +++ b/primitiveCollections/src/main/java/org/lucares/collections/IntList.java @@ -202,51 +202,118 @@ public final class IntList implements Serializable, Cloneable { /** * Inserts {@code values} at position {@code pos} into the list. * - * @param pos the position to insert the elements - * @param values the elements to insert + * @param destPos the position to insert the elements + * @param values the elements to insert * @throws IndexOutOfBoundsException if pos is out of bounds - * {@code pos < 0 || pos > size()} * @throws NullPointerException if the given array is null */ - public void insert(final int pos, final int... values) { + public void insert(final int destPos, final IntList values) { + final TriValueBool sourceIsSorted = values.isSorted() ? TriValueBool.TRUE : TriValueBool.FALSE; + insert(destPos, values.data, 0, values.size(), sourceIsSorted); + } - if (pos < 0) { - throw new IndexOutOfBoundsException("pos must not be negative, but was: " + pos); - } - if (pos > size) { - throw new IndexOutOfBoundsException("pos must be smaller than size(), but was: " + pos); - } - if (values == null) { - throw new NullPointerException("values parameter must not be null"); - } - if (values.length == 0) { + /** + * Inserts {@code values} at position {@code pos} into the list. + * + * @param destPos the position to insert the elements + * @param source the elements to insert + * @param sourcePos starting position in the source array + * @param length number of elements to insert + * @throws IndexOutOfBoundsException if destPos is out of bounds, if sourcePos + * is out of bounds or if sourcePos + length + * is out of bounds + * @throws NullPointerException if the given array is null + */ + public void insert(final int destPos, final IntList source, final int sourcePos, final int length) { + + // note: the sublist might be sorted even if the complete list is not + final TriValueBool sourceIsSorted = source.isSorted() ? TriValueBool.TRUE : TriValueBool.UNKNOWN; + insert(destPos, source.data, sourcePos, length, sourceIsSorted); + } + + /** + * Inserts {@code values} at position {@code pos} into the list. + * + * @param destPos the position to insert the elements + * @param source the elements to insert + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws NullPointerException if the given array is null + */ + public void insert(final int destPos, final int... source) { + insert(destPos, source, 0, source.length, TriValueBool.UNKNOWN); + } + + /** + * Inserts {@code values} at position {@code pos} into the list. + * + * @param destPos the position to insert the elements + * @param source the elements to insert + * @param sourcePos starting position in the source array + * @param length number of elements to insert + * @throws IndexOutOfBoundsException if destPos is out of bounds, if sourcePos + * is out of bounds or if sourcePos + length + * is out of bounds + * @throws NullPointerException if the given array is null + */ + public void insert(final int destPos, final int[] source, final int sourcePos, final int length) { + insert(destPos, source, sourcePos, length, TriValueBool.UNKNOWN); + } + + private void insert(final int destPos, final int[] source, final int sourcePos, final int length, + final TriValueBool sourceIsSorted) { + + if (length == 0) { return; } + if (destPos < 0) { + throw new IndexOutOfBoundsException("destPos must not be negative, but was: " + destPos); + } + if (destPos > size) { + throw new IndexOutOfBoundsException("destPos must not be larger than size(), but was: " + sourcePos); + } + if (sourcePos >= source.length) { + throw new IndexOutOfBoundsException("sourcePos must be smaller than source.length, but was: " + destPos); + } + if (sourcePos + length > source.length) { + throw new IndexOutOfBoundsException( + "sourcePos + length must not be larger than source.length, but was: " + (sourcePos + length)); + } - ensureCapacity(values.length); + ensureCapacity(length); // move everything after the insert position to make room for the new values - System.arraycopy(data, pos, data, pos + values.length, size - pos); + System.arraycopy(data, destPos, data, destPos + length, size - destPos); // insert the new values - System.arraycopy(values, 0, data, pos, values.length); - size += values.length; + System.arraycopy(source, sourcePos, data, destPos, length); + size += length; if (sorted) { // compare with element before the insertion - sorted = pos == 0 ? sorted : data[pos - 1] <= data[pos]; + sorted = destPos == 0 ? sorted : data[destPos - 1] <= data[destPos]; // check if the inserted values are sorted - for (int i = 1; i < values.length && sorted; i++) { - sorted = data[pos + i - 1] <= data[pos + i]; + switch (sourceIsSorted) { + case TRUE: + // nothing to do, keep the current value of 'sorted' + break; + case FALSE: + sorted = false; + break; + case UNKNOWN: + for (int i = 1; i < length && sorted; i++) { + sorted = data[destPos + i - 1] <= data[destPos + i]; + } + break; + default: + throw new IllegalStateException("unhandled enum value: " + sourceIsSorted); } if (sorted) { // compare last inserted element with next element in the list - sorted = pos + values.length < size()// - ? data[pos + values.length - 1] <= data[pos + values.length]// + sorted = destPos + length < size()// + ? data[destPos + length - 1] <= data[destPos + length]// : sorted; } } - } /** diff --git a/primitiveCollections/src/main/java/org/lucares/collections/LongList.java b/primitiveCollections/src/main/java/org/lucares/collections/LongList.java index b5b4dc8..1378f89 100644 --- a/primitiveCollections/src/main/java/org/lucares/collections/LongList.java +++ b/primitiveCollections/src/main/java/org/lucares/collections/LongList.java @@ -192,51 +192,118 @@ public final class LongList implements Serializable, Cloneable { /** * Inserts {@code values} at position {@code pos} into the list. * - * @param pos the position to insert the elements - * @param values the elements to insert - * @throws IndexOutOfBoundsException if pos is out of bounds - * {@code pos < 0 || pos > size()} + * @param destPos the position to insert the elements + * @param values the elements to insert + * @throws IndexOutOfBoundsException if destPos is out of bounds * @throws NullPointerException if the given array is null */ - public void insert(final int pos, final long... values) { + public void insert(final int destPos, final LongList values) { + final TriValueBool sourceIsSorted = values.isSorted() ? TriValueBool.TRUE : TriValueBool.FALSE; + insert(destPos, values.data, 0, values.size(), sourceIsSorted); + } - if (pos < 0) { - throw new IndexOutOfBoundsException("pos must not be negative, but was: " + pos); - } - if (pos > size) { - throw new IndexOutOfBoundsException("pos must be smaller than size(), but was: " + pos); - } - if (values == null) { - throw new NullPointerException("values parameter must not be null"); - } - if (values.length == 0) { + /** + * Inserts {@code values} at position {@code pos} into the list. + * + * @param destPos the position to insert the elements + * @param source the elements to insert + * @param sourcePos starting position in the source array + * @param length number of elements to insert + * @throws IndexOutOfBoundsException if destPos is out of bounds, if sourcePos + * is out of bounds or if sourcePos + length + * is out of bounds + * @throws NullPointerException if the given array is null + */ + public void insert(final int destPos, final LongList source, final int sourcePos, final int length) { + + // note: the sublist might be sorted even if the complete list is not + final TriValueBool sourceIsSorted = source.isSorted() ? TriValueBool.TRUE : TriValueBool.UNKNOWN; + insert(destPos, source.data, sourcePos, length, sourceIsSorted); + } + + /** + * Inserts {@code values} at position {@code pos} into the list. + * + * @param destPos the position to insert the elements + * @param source the elements to insert + * @throws IndexOutOfBoundsException if destPos is out of bounds + * @throws NullPointerException if the given array is null + */ + public void insert(final int destPos, final long... source) { + insert(destPos, source, 0, source.length, TriValueBool.UNKNOWN); + } + + /** + * Inserts {@code values} at position {@code pos} into the list. + * + * @param destPos the position to insert the elements + * @param source the elements to insert + * @param sourcePos starting position in the source array + * @param length number of elements to insert + * @throws IndexOutOfBoundsException if destPos is out of bounds, if sourcePos + * is out of bounds or if sourcePos + length + * is out of bounds + * @throws NullPointerException if the given array is null + */ + public void insert(final int destPos, final long[] source, final int sourcePos, final int length) { + insert(destPos, source, sourcePos, length, TriValueBool.UNKNOWN); + } + + private void insert(final int destPos, final long[] source, final int sourcePos, final int length, + final TriValueBool sourceIsSorted) { + + if (length == 0) { return; } + if (destPos < 0) { + throw new IndexOutOfBoundsException("destPos must not be negative, but was: " + destPos); + } + if (destPos > size) { + throw new IndexOutOfBoundsException("destPos must not be larger than size(), but was: " + sourcePos); + } + if (sourcePos >= source.length) { + throw new IndexOutOfBoundsException("sourcePos must be smaller than source.length, but was: " + destPos); + } + if (sourcePos + length > source.length) { + throw new IndexOutOfBoundsException( + "sourcePos + length must not be larger than source.length, but was: " + (sourcePos + length)); + } - ensureCapacity(values.length); + ensureCapacity(length); // move everything after the insert position to make room for the new values - System.arraycopy(data, pos, data, pos + values.length, size - pos); + System.arraycopy(data, destPos, data, destPos + length, size - destPos); // insert the new values - System.arraycopy(values, 0, data, pos, values.length); - size += values.length; + System.arraycopy(source, sourcePos, data, destPos, length); + size += length; if (sorted) { // compare with element before the insertion - sorted = pos == 0 ? sorted : data[pos - 1] <= data[pos]; + sorted = destPos == 0 ? sorted : data[destPos - 1] <= data[destPos]; // check if the inserted values are sorted - for (int i = 1; i < values.length && sorted; i++) { - sorted = data[pos + i - 1] <= data[pos + i]; + switch (sourceIsSorted) { + case TRUE: + // nothing to do, keep the current value of 'sorted' + break; + case FALSE: + sorted = false; + break; + case UNKNOWN: + for (int i = 1; i < length && sorted; i++) { + sorted = data[destPos + i - 1] <= data[destPos + i]; + } + break; + default: + throw new IllegalStateException("unhandled enum value: " + sourceIsSorted); } if (sorted) { // compare last inserted element with next element in the list - sorted = pos + values.length < size()// - ? data[pos + values.length - 1] <= data[pos + values.length]// + sorted = destPos + length < size()// + ? data[destPos + length - 1] <= data[destPos + length]// : sorted; } } - } /** diff --git a/primitiveCollections/src/main/java/org/lucares/collections/TriValueBool.java b/primitiveCollections/src/main/java/org/lucares/collections/TriValueBool.java new file mode 100644 index 0000000..cdf9fe8 --- /dev/null +++ b/primitiveCollections/src/main/java/org/lucares/collections/TriValueBool.java @@ -0,0 +1,8 @@ +package org.lucares.collections; + +/** + * Represents a boolean with three values: true, false and unknown + */ +enum TriValueBool { + TRUE, FALSE, UNKNOWN +} diff --git a/primitiveCollections/src/test/java/org/lucares/collections/IntListTest.java b/primitiveCollections/src/test/java/org/lucares/collections/IntListTest.java index 3e4a90b..56eea9c 100644 --- a/primitiveCollections/src/test/java/org/lucares/collections/IntListTest.java +++ b/primitiveCollections/src/test/java/org/lucares/collections/IntListTest.java @@ -132,24 +132,91 @@ public class IntListTest { list.insert(0); Assertions.assertArrayEquals(new int[] {}, list.toArray()); + Assertions.assertTrue(list.isSorted()); list.insert(0, 1); Assertions.assertArrayEquals(new int[] { 1 }, list.toArray()); + Assertions.assertTrue(list.isSorted()); list.insert(1, 2, 2, 2); Assertions.assertArrayEquals(new int[] { 1, 2, 2, 2 }, list.toArray()); + Assertions.assertTrue(list.isSorted()); list.insert(1, 3); Assertions.assertArrayEquals(new int[] { 1, 3, 2, 2, 2 }, list.toArray()); + Assertions.assertFalse(list.isSorted()); list.insert(2, 4, 4, 4); Assertions.assertArrayEquals(new int[] { 1, 3, 4, 4, 4, 2, 2, 2 }, list.toArray()); + Assertions.assertFalse(list.isSorted()); list.insert(2); Assertions.assertArrayEquals(new int[] { 1, 3, 4, 4, 4, 2, 2, 2 }, list.toArray()); + Assertions.assertFalse(list.isSorted()); list.insert(8, 5, 5); Assertions.assertArrayEquals(new int[] { 1, 3, 4, 4, 4, 2, 2, 2, 5, 5 }, list.toArray()); + Assertions.assertFalse(list.isSorted()); + } + + @Test + public void testInsertWithFlexibleApi() { + final IntList list = new IntList(); + + list.insert(0, new int[] {}, 0, 0); + Assertions.assertArrayEquals(new int[] {}, list.toArray()); + + list.insert(0, new int[] { 1, 3 }, 0, 2); + Assertions.assertArrayEquals(new int[] { 1, 3 }, list.toArray()); + Assertions.assertTrue(list.isSorted()); + + list.insert(1, new int[] { -1, 2, -1 }, 1, 1); // only the 2 will be inserted + Assertions.assertArrayEquals(new int[] { 1, 2, 3 }, list.toArray()); + Assertions.assertTrue(list.isSorted()); + + list.insert(2, new int[] { 7, 8 }, 0, 2); + Assertions.assertArrayEquals(new int[] { 1, 2, 7, 8, 3 }, list.toArray()); + Assertions.assertFalse(list.isSorted()); + } + + @Test + public void testInsertIntListWithFlexibleApi() { + final IntList list = new IntList(); + + list.insert(0, IntList.of(), 0, 0); + Assertions.assertArrayEquals(new int[] {}, list.toArray()); + + list.insert(0, IntList.of(1, 3), 0, 2); + Assertions.assertArrayEquals(new int[] { 1, 3 }, list.toArray()); + Assertions.assertTrue(list.isSorted()); + + list.insert(1, IntList.of(-1, 2, -1), 1, 1); // only the 2 will be inserted + Assertions.assertArrayEquals(new int[] { 1, 2, 3 }, list.toArray()); + Assertions.assertTrue(list.isSorted()); + + list.insert(2, IntList.of(7, 8), 0, 2); + Assertions.assertArrayEquals(new int[] { 1, 2, 7, 8, 3 }, list.toArray()); + Assertions.assertFalse(list.isSorted()); + } + + @Test + public void testInsertIntListWithSimpleApi() { + final IntList list = new IntList(); + + list.insert(0, IntList.of()); + Assertions.assertArrayEquals(new int[] {}, list.toArray()); + + list.insert(0, IntList.of(1, 3)); + Assertions.assertArrayEquals(new int[] { 1, 3 }, list.toArray()); + Assertions.assertTrue(list.isSorted()); + + list.insert(1, IntList.of(2)); + Assertions.assertArrayEquals(new int[] { 1, 2, 3 }, list.toArray()); + Assertions.assertTrue(list.isSorted()); + + list.insert(3, IntList.of(6, 5)); + Assertions.assertArrayEquals(new int[] { 1, 2, 3, 6, 5 }, list.toArray()); + Assertions.assertFalse(list.isSorted()); } @Test @@ -176,13 +243,45 @@ public class IntListTest { } catch (final IndexOutOfBoundsException e) { // expected } + try { + // sourcePos is out of range + list.insert(1, IntList.of(1), 1, 1); + Assertions.fail(); + } catch (final IndexOutOfBoundsException e) { + // expected + } + try { + // length is out of range + list.insert(1, IntList.of(1), 0, 2); + Assertions.fail(); + } catch (final IndexOutOfBoundsException e) { + // expected + } + try { + // sourcePod+length is out of range + list.insert(1, IntList.of(1, 2, 3), 2, 2); + Assertions.fail(); + } catch (final IndexOutOfBoundsException e) { + // expected + } } @Test - public void testInsertNull() { + public void testInsertNullArray() { final IntList list = new IntList(); try { - list.insert(0, null); + list.insert(0, (int[]) null); + Assertions.fail(); + } catch (final NullPointerException e) { + // expected + } + } + + @Test + public void testInsertNullList() { + final IntList list = new IntList(); + try { + list.insert(0, (IntList) null); Assertions.fail(); } catch (final NullPointerException e) { // expected diff --git a/primitiveCollections/src/test/java/org/lucares/collections/LongListTest.java b/primitiveCollections/src/test/java/org/lucares/collections/LongListTest.java index ca32d9e..eca9207 100644 --- a/primitiveCollections/src/test/java/org/lucares/collections/LongListTest.java +++ b/primitiveCollections/src/test/java/org/lucares/collections/LongListTest.java @@ -79,8 +79,8 @@ public class LongListTest { @Test public void testRangeClosed() { - final IntList expected = IntList.of(1, 2, 3, 4, 5); - final IntList list = IntList.rangeClosed(1, 5); + final LongList expected = LongList.of(1, 2, 3, 4, 5); + final LongList list = LongList.rangeClosed(1, 5); Assertions.assertArrayEquals(expected.toArray(), list.toArray()); Assertions.assertEquals(5, list.getCapacity()); } @@ -160,6 +160,66 @@ public class LongListTest { Assertions.assertArrayEquals(new long[] { 1, 3, 4, 4, 4, 2, 2, 2, 5, 5 }, list.toArray()); } + @Test + public void testInsertWithFlexibleApi() { + final LongList list = new LongList(); + + list.insert(0, new long[] {}, 0, 0); + Assertions.assertArrayEquals(new long[] {}, list.toArray()); + + list.insert(0, new long[] { 1, 3 }, 0, 2); + Assertions.assertArrayEquals(new long[] { 1, 3 }, list.toArray()); + Assertions.assertTrue(list.isSorted()); + + list.insert(1, new long[] { -1, 2, -1 }, 1, 1); // only the 2 will be inserted + Assertions.assertArrayEquals(new long[] { 1, 2, 3 }, list.toArray()); + Assertions.assertTrue(list.isSorted()); + + list.insert(2, new long[] { 7, 8 }, 0, 2); + Assertions.assertArrayEquals(new long[] { 1, 2, 7, 8, 3 }, list.toArray()); + Assertions.assertFalse(list.isSorted()); + } + + @Test + public void testInsertLongListWithFlexibleApi() { + final LongList list = new LongList(); + + list.insert(0, LongList.of(), 0, 0); + Assertions.assertArrayEquals(new long[] {}, list.toArray()); + + list.insert(0, LongList.of(1, 3), 0, 2); + Assertions.assertArrayEquals(new long[] { 1, 3 }, list.toArray()); + Assertions.assertTrue(list.isSorted()); + + list.insert(1, LongList.of(-1, 2, -1), 1, 1); // only the 2 will be inserted + Assertions.assertArrayEquals(new long[] { 1, 2, 3 }, list.toArray()); + Assertions.assertTrue(list.isSorted()); + + list.insert(2, LongList.of(7, 8), 0, 2); + Assertions.assertArrayEquals(new long[] { 1, 2, 7, 8, 3 }, list.toArray()); + Assertions.assertFalse(list.isSorted()); + } + + @Test + public void testInsertLongListWithSimpleApi() { + final LongList list = new LongList(); + + list.insert(0, LongList.of()); + Assertions.assertArrayEquals(new long[] {}, list.toArray()); + + list.insert(0, LongList.of(1, 3)); + Assertions.assertArrayEquals(new long[] { 1, 3 }, list.toArray()); + Assertions.assertTrue(list.isSorted()); + + list.insert(1, LongList.of(2)); + Assertions.assertArrayEquals(new long[] { 1, 2, 3 }, list.toArray()); + Assertions.assertTrue(list.isSorted()); + + list.insert(3, LongList.of(6, 5)); + Assertions.assertArrayEquals(new long[] { 1, 2, 3, 6, 5 }, list.toArray()); + Assertions.assertFalse(list.isSorted()); + } + @Test public void testInsertOutOfBounds() { final LongList list = new LongList(); @@ -184,13 +244,34 @@ public class LongListTest { } catch (final IndexOutOfBoundsException e) { // expected } + try { + // sourcePos is out of range + list.insert(1, LongList.of(1), 1, 1); + Assertions.fail(); + } catch (final IndexOutOfBoundsException e) { + // expected + } + try { + // length is out of range + list.insert(1, LongList.of(1), 0, 2); + Assertions.fail(); + } catch (final IndexOutOfBoundsException e) { + // expected + } + try { + // sourcePod+length is out of range + list.insert(1, LongList.of(1, 2, 3), 2, 2); + Assertions.fail(); + } catch (final IndexOutOfBoundsException e) { + // expected + } } @Test public void testInsertNull() { final LongList list = new LongList(); try { - list.insert(0, null); + list.insert(0, (long[]) null); Assertions.fail(); } catch (final NullPointerException e) { // expected