add union(IntList,IntList) and addAll(IntList)

IntPredicate gets the current value and the index.
This was handy while removing duplicate values.
This commit is contained in:
2017-12-12 18:45:11 +01:00
parent 3dd1955749
commit 76e5dc403c
3 changed files with 296 additions and 29 deletions

View File

@@ -21,7 +21,14 @@ public final class IntList implements Serializable, Cloneable {
// TODO add mod counts
// TODO support sublists
// TODO add lastIndexOf
// TODO check that methods like intersection/union work with big arrays. They
// should not try to allocate more memory MAX_ARRAY_SIZE
// TODO remove bounds checks that are handled by java, e.g. negative indices
// TODO removeIf see ArrayList.removeIf. Cannot handle removeIf(i -> indexOf(i)
// != lastIndexOf(i)) on sorted lists
// TODO wrapper that reverses the order. This implies, that IntList becomes an
// interface, or it could not be final anymore. Being an interface would make it
// possible to have unmodifiable lists, too.
private static final long serialVersionUID = 2622570032686034909L;
@@ -81,9 +88,8 @@ public final class IntList implements Serializable, Cloneable {
* if the specified {@link IntList} is null
*/
public IntList(final IntList intList) {
data = Arrays.copyOf(intList.data, intList.size());
size = intList.size();
sorted = intList.sorted;
data = EMPTY_ARRAY;
addAll(intList);
}
/**
@@ -261,6 +267,36 @@ public final class IntList implements Serializable, Cloneable {
size += values.length;
}
/**
* Add all value of the given list.
*
* @param list
* the list
* @throws NullPointerException
* if the given list is null
*/
public void addAll(final IntList list) {
ensureCapacity(list.size());
System.arraycopy(list.data, 0, data, size, list.size());
if (sorted) {
sorted = list.isSorted();
if (list.isSorted() // new list is sorted
&& !isEmpty() // if old list is empty, then the new list's sorted value is equal to that of
// the new list
&& !list.isEmpty()) // if new list is empty, then old list stays sorted
{
// check that the first new value is bigger than the highest old value
final int highestOldValue = data[size - 1]; // size has still the old value
final int lowestNewValue = list.get(0);
sorted = highestOldValue <= lowestNewValue;
}
}
size += list.size();
}
/**
* Removes elements from the list.
* <p>
@@ -344,7 +380,7 @@ public final class IntList implements Serializable, Cloneable {
int insertPosition = 0;
for (int i = 0; i < size; i++) {
final int current = data[i];
if (!predicate.test(current)) {
if (!predicate.test(current, i)) {
// keep current element
data[insertPosition] = current;
insertPosition++;
@@ -598,8 +634,8 @@ public final class IntList implements Serializable, Cloneable {
}
/**
* Returns the index of the first occurrence of {@code value} starting at the
* {@code offset}'s element, or -1 if it does not exist.
* Returns the index of the last occurrence of {@code value}, or -1 if it does
* not exist.
* <p>
* This method uses a binary search algorithm if the list is sorted.
*
@@ -610,6 +646,8 @@ public final class IntList implements Serializable, Cloneable {
* @return the index, or -1
* @throws ArrayIndexOutOfBoundsException
* if offset is negative, or larger than the size of the list
* @throws IllegalArgumentException
* if offset is negative
* @see #isSorted()
*/
public int indexOf(final int value, final int offset) {
@@ -617,7 +655,7 @@ public final class IntList implements Serializable, Cloneable {
int result = -1;
if (sorted) {
int insertionPoint = Arrays.binarySearch(data, offset, size(), value);
while (insertionPoint > 0 && insertionPoint > offset && data[insertionPoint - 1] == value) {
while (insertionPoint > offset && data[insertionPoint - 1] == value) {
insertionPoint--;
}
result = insertionPoint < 0 ? -1 : insertionPoint;
@@ -760,12 +798,14 @@ public final class IntList implements Serializable, Cloneable {
* method returns a new list. The list can have unused capacity. Consider using
* {@link #trim()}.
* <p>
* If both lists were sorted, then the output list will also be sorted.
* <p>
* See {@link #retainAll(IntList)} for a method that modifies the list and keeps
* duplicate values.
* <p>
* If both lists are sorted, then the complexity is O(n+m), where n is the
* If both lists are sorted, then the time complexity is O(n+m), where n is the
* length of the first list and m the length of the second list. If at least one
* list is not sorted, then the complexity is O(n*log(m)), where n is the length
* list is not sorted, then the time complexity is O(n*m), where n is the length
* of the shorter list and m the length of the longer list.
*
* @param a
@@ -834,8 +874,8 @@ public final class IntList implements Serializable, Cloneable {
}
/**
* Implements an algorithm with O(n*log(m)), where n is the length of the
* shorter list and m the length of the longer list.
* Implements an algorithm with O(n*m), where n is the length of the shorter
* list and m the length of the longer list.
*
* @param a
* first list
@@ -851,8 +891,6 @@ public final class IntList implements Serializable, Cloneable {
if (aSize < bSize) {
result = new IntList(Math.min(aSize, bSize));
assert !a.isSorted() || !b.isSorted() : "at least one list must be unsorted";
for (int l = 0; l < aSize; l++) {
final int lv = a.get(l);
@@ -870,4 +908,104 @@ public final class IntList implements Serializable, Cloneable {
return result;
}
/**
* Returns a list with all elements that are in list {@code a} or {@code b}
* (logical or).
* <p>
* The result does not contain duplicate values.
* <p>
* If both lists were sorted, then the output list will also be sorted. If at
* least one list is unsorted, then the order is undefined.
* <p>
* If both lists are sorted, then the time complexity is O(n+m), where n is the
* length of the first list and m the length of the second list. If at least one
* list is not sorted, then the time complexity is O((n+m)*log(n+m)), where n is
* the length of the shorter list and m the length of the longer list.
*
* @param a
* the first list
* @param b
* the second list
* @return the union of both lists
*/
public static IntList union(final IntList a, final IntList b) {
final IntList result;
if (a.isSorted() && b.isSorted()) {
result = unionSorted(a, b);
} else {
result = unionUnsorted(a, b);
}
return result;
}
private static IntList unionSorted(final IntList a, final IntList b) {
final IntList result = new IntList(a.size() + b.size());
final int aSize = a.size();
final int bSize = b.size();
int l = 0;
int r = 0;
while (l < a.size() && r < b.size()) {
final int lv = a.get(l);
final int rv = b.get(r);
if (lv < rv) {
result.add(lv);
l++;
while (l < aSize && lv == a.get(l)) {
l++;
}
} else if (lv > rv) {
result.add(rv);
r++;
while (r < bSize && rv == b.get(r)) {
r++;
}
} else {
result.add(lv);
l++;
r++;
while (l < aSize && lv == a.get(l)) {
l++;
}
while (r < bSize && rv == b.get(r)) {
r++;
}
}
}
// add remaining values from list a, if any exist
for (; l < aSize; l++) {
if (l == 0 || a.get(l) != a.get(l - 1)) {
result.add(a.get(l));
}
}
// add remaining values from list b, if any exist
for (; r < bSize; r++) {
if (r == 0 || b.get(r) != b.get(r - 1)) {
result.add(b.get(r));
}
}
return result;
}
private static IntList unionUnsorted(final IntList a, final IntList b) {
// TODO use a more efficient algorithm. Especially the removeIf is too
// expensive, because of all the method calls
final IntList result;
result = new IntList();
result.addAll(a);
result.addAll(b);
result.sort();
result.removeIf((value, index) -> index > 0 && result.get(index) == result.get(index - 1));
return result;
}
}

View File

@@ -11,11 +11,13 @@ public interface IntPredicate {
/**
* Evaluates the predicate.
*
* @param i
* the input argument
* @param value
* the value
* @param index
* the index in the list
* @return {@code true} iff the input argument matches the predicate
*/
boolean test(int i);
boolean test(int value, int index);
/**
* Returns a predicate that represents the logical AND of {@code this} and
@@ -28,7 +30,7 @@ public interface IntPredicate {
* if {@code other} is null
*/
default IntPredicate and(final IntPredicate other) {
return (t) -> test(t) && other.test(t);
return (value, index) -> test(value, index) && other.test(value, index);
}
/**
@@ -42,7 +44,7 @@ public interface IntPredicate {
* if {@code other} is null
*/
default IntPredicate or(final IntPredicate other) {
return (t) -> test(t) || other.test(t);
return (value, index) -> test(value, index) || other.test(value, index);
}
/**
@@ -51,6 +53,6 @@ public interface IntPredicate {
* @return the negation of {@code this}
*/
default IntPredicate negate() {
return (t) -> !test(t);
return (value, index) -> !test(value, index);
}
}

View File

@@ -224,6 +224,64 @@ public class IntListTest {
}
}
@Test
public void testAddList() {
final IntList list = new IntList();
// adding empty list with no capacity
list.addAll(IntList.of());
Assert.assertEquals(new IntList(), list);
Assert.assertTrue(list.isSorted());
// adding empty list with capacity 2
list.addAll(new IntList(2));
Assert.assertEquals(new IntList(), list);
Assert.assertTrue(list.isSorted());
// adding sorted list to an empty list
list.addAll(IntList.of(1, 2, 3));
Assert.assertEquals(IntList.of(1, 2, 3), list);
Assert.assertTrue(list.isSorted());
// add empty list to a sorted list
list.addAll(IntList.of());
Assert.assertEquals(IntList.of(1, 2, 3), list);
Assert.assertTrue(list.isSorted());
// adding sorted list to a sorted list so that the list stays sorted
list.addAll(IntList.of(3, 4, 5));
Assert.assertEquals(IntList.of(1, 2, 3, 3, 4, 5), list);
Assert.assertTrue(list.isSorted());
// adding sorted list to a sorted list, but the new list is not sorted
list.clear();
list.addAll(IntList.of(1, 2, 3));
list.addAll(IntList.of(0));
Assert.assertEquals(IntList.of(1, 2, 3, 0), list);
Assert.assertFalse(list.isSorted());
// adding unsorted list to a sorted list
list.clear();
list.addAll(IntList.of(1, 2, 3));
list.addAll(IntList.of(6, 5, 4));
Assert.assertEquals(IntList.of(1, 2, 3, 6, 5, 4), list);
Assert.assertFalse(list.isSorted());
// adding unsorted list to an empty list
list.clear();
list.addAll(IntList.of(3, 2, 1));
Assert.assertEquals(IntList.of(3, 2, 1), list);
Assert.assertFalse(list.isSorted());
// adding sorted list to an unsorted list
list.clear();
list.addAll(IntList.of(3, 2, 1));
list.addAll(IntList.of(1, 2, 3));
Assert.assertEquals(IntList.of(3, 2, 1, 1, 2, 3), list);
Assert.assertFalse(list.isSorted());
}
@Test
public void testGetArray() {
final IntList list = new IntList();
@@ -806,7 +864,7 @@ public class IntListTest {
public void testRemoveIf() {
final IntList list = IntList.of(1, 2, 3, 4, 5, 6);
list.removeIf(i -> i % 2 == 0);
list.removeIf((value, index) -> value % 2 == 0);
Assert.assertArrayEquals(new int[] { 1, 3, 5 }, list.toArray());
}
@@ -814,7 +872,7 @@ public class IntListTest {
public void testRemoveIfNegationOfPredicate() {
final IntList list = IntList.of(1, 2, 3, 4, 5, 6);
final IntPredicate predicate = i -> i % 2 == 0;
final IntPredicate predicate = (value, index) -> value % 2 == 0;
list.removeIf(predicate.negate());
Assert.assertArrayEquals(new int[] { 2, 4, 6 }, list.toArray());
}
@@ -823,8 +881,8 @@ public class IntListTest {
public void testRemoveIfWithAndCombinedPredicates() {
final IntList list = IntList.of(1, 2, 3, 4, 5, 6);
final IntPredicate predicateA = i -> i % 2 == 0;
final IntPredicate predicateB = i -> i == 3 || i == 4;
final IntPredicate predicateA = (value, index) -> value % 2 == 0;
final IntPredicate predicateB = (value, index) -> value == 3 || value == 4;
list.removeIf(predicateA.and(predicateB));
Assert.assertArrayEquals(new int[] { 1, 2, 3, 5, 6 }, list.toArray());
}
@@ -833,8 +891,8 @@ public class IntListTest {
public void testRemoveIfWithOrCombinedPredicates() {
final IntList list = IntList.of(1, 2, 3, 4, 5, 6);
final IntPredicate predicateA = i -> i % 2 == 0;
final IntPredicate predicateB = i -> i == 3 || i == 4;
final IntPredicate predicateA = (value, index) -> value % 2 == 0;
final IntPredicate predicateB = (value, index) -> value == 3 || value == 4;
list.removeIf(predicateA.or(predicateB));
Assert.assertArrayEquals(new int[] { 1, 5 }, list.toArray());
}
@@ -843,7 +901,7 @@ public class IntListTest {
public void testRemoveIfOnEmptyList() {
final IntList list = IntList.of();
list.removeIf(i -> false);
list.removeIf((value, index) -> false);
Assert.assertArrayEquals(new int[] {}, list.toArray());
}
@@ -1067,14 +1125,14 @@ public class IntListTest {
public void testSortedFlagRemoveIf() {
final IntList list = IntList.of(4, 3, 2, 1);
list.removeIf(v -> v >= 3); // removes 3 and 4
list.removeIf((value, index) -> value >= 3); // removes 3 and 4
Assert.assertFalse("unsorted list with two elements is not sorted", list.isSorted());
list.removeIf(v -> v >= 2); // removes 2
list.removeIf((value, index) -> value >= 2); // removes 2
Assert.assertTrue("unsorted list with one element becomes sorted", list.isSorted());
list.add(-1); // make list unsorted again
list.removeIf(v -> true); // remove both elements
list.removeIf((value, index) -> true); // remove both elements
Assert.assertTrue("unsorted list with no elements becomes sorted", list.isSorted());
}
@@ -1153,4 +1211,73 @@ public class IntListTest {
Assert.assertEquals(IntList.of(4), actual);
}
}
@Test
public void testUnionSortedLists_emptyLists() {
final IntList a = IntList.of();
final IntList b = IntList.of();
Assert.assertEquals(IntList.of(), IntList.union(a, b));
Assert.assertEquals(IntList.union(a, b), IntList.union(b, a));
}
@Test
public void testUnionSortedLists_uniqueValues() {
final IntList a = IntList.of(0, 1, 3, 4);
final IntList b = IntList.of(2, 4, 5);
final IntList actual = IntList.union(a, b);
Assert.assertEquals(IntList.of(0, 1, 2, 3, 4, 5), actual);
Assert.assertEquals(IntList.union(a, b), IntList.union(b, a));
}
@Test
public void testUnionSortedLists_duplicateValues_inMiddleOfListA() {
final IntList a = IntList.of(1, 2, 2, 3);
final IntList b = IntList.of(1, 3);
final IntList actual = IntList.union(a, b);
Assert.assertEquals(IntList.of(1, 2, 3), actual);
Assert.assertEquals(IntList.union(a, b), IntList.union(b, a));
}
@Test
public void testUnionSortedLists_duplicateValues_inMiddleOfBothLists() {
final IntList a = IntList.of(1, 2, 2, 3);
final IntList b = IntList.of(1, 2, 2, 4);
final IntList actual = IntList.union(a, b);
Assert.assertEquals(IntList.of(1, 2, 3, 4), actual);
Assert.assertEquals(IntList.union(a, b), IntList.union(b, a));
}
@Test
public void testUnionSortedLists_duplicateValues_atEndOfListA_whenHighestValueInBIsSmaller() {
final IntList a = IntList.of();
final IntList b = IntList.of(2, 2);
final IntList actual = IntList.union(a, b);
Assert.assertEquals(IntList.of(2), actual);
Assert.assertEquals(IntList.union(a, b), IntList.union(b, a));
}
@Test
public void testUnionUnsortedLists() {
final IntList a = IntList.of(1, 0, 3, 4);
final IntList b = IntList.of(2, 5, 4);
final IntList actual = IntList.union(a, b);
Assert.assertEquals(IntList.of(0, 1, 2, 3, 4, 5), actual);
Assert.assertEquals(IntList.union(a, b), IntList.union(b, a));
}
@Test
public void testUnionUnsortedLists_oneListIsSorted() {
final IntList a = IntList.of(1, 2, 3);
final IntList b = IntList.of(2, 5, 4);
final IntList actual = IntList.union(a, b);
Assert.assertEquals(IntList.of(1, 2, 3, 4, 5), actual);
Assert.assertEquals(IntList.union(a, b), IntList.union(b, a));
}
}