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);
}
}