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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user