add 'sorted' flag that keeps track whether the list is sorted
The flag can be used in indexOf or for a better retainAll implementation.
This commit is contained in:
@@ -18,9 +18,10 @@ public final class IntList implements Serializable, Cloneable {
|
||||
// TODO support Iterator
|
||||
// TODO add mod counts
|
||||
// TODO support sublists
|
||||
// TODO add remember if list is sorted so we can use binary search in indexOf
|
||||
// TODO use sorted flag in indexOf
|
||||
// TODO add lastIndexOf
|
||||
// TODO remove bounds checks that are handled by java, e.g. negative indices
|
||||
// TODO clear
|
||||
|
||||
private static final long serialVersionUID = 2622570032686034909L;
|
||||
|
||||
@@ -41,6 +42,12 @@ public final class IntList implements Serializable, Cloneable {
|
||||
|
||||
private int index = 0;
|
||||
|
||||
/**
|
||||
* Keeps track of whether or not the list is sorted. This allows us to use
|
||||
* binary search for {@link #indexOf(int)}. An empty list is sorted.
|
||||
*/
|
||||
private boolean sorted = true;
|
||||
|
||||
/**
|
||||
* Create a new {@link IntList} with initial capacity 10.
|
||||
*/
|
||||
@@ -77,6 +84,7 @@ public final class IntList implements Serializable, Cloneable {
|
||||
data = new int[intList.getCapacity()];
|
||||
System.arraycopy(intList.data, 0, data, 0, intList.size());
|
||||
index = intList.size();
|
||||
sorted = intList.sorted;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,6 +131,17 @@ public final class IntList implements Serializable, Cloneable {
|
||||
return data.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the list is sorted.
|
||||
* <p>
|
||||
* An empty list is sorted.
|
||||
*
|
||||
* @return true iff the list is sorted.
|
||||
*/
|
||||
public boolean isSorted() {
|
||||
return sorted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds {@code value} to the list.
|
||||
*
|
||||
@@ -133,6 +152,10 @@ public final class IntList implements Serializable, Cloneable {
|
||||
ensureCapacity(1);
|
||||
|
||||
data[index] = value;
|
||||
|
||||
if (sorted) {
|
||||
sorted = index == 0 ? sorted : data[index - 1] <= data[index];
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
@@ -159,15 +182,34 @@ public final class IntList implements Serializable, Cloneable {
|
||||
if (values == null) {
|
||||
throw new NullPointerException("values parameter must not be null");
|
||||
}
|
||||
if (values.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ensureCapacity(values.length);
|
||||
|
||||
// move everything after the insert position to make room for the new values
|
||||
System.arraycopy(data, pos, data, pos + values.length, values.length);
|
||||
System.arraycopy(data, pos, data, pos + values.length, index - pos);
|
||||
// insert the new values
|
||||
System.arraycopy(values, 0, data, pos, values.length);
|
||||
|
||||
index += values.length;
|
||||
|
||||
if (sorted) {
|
||||
// compare with element before the insertion
|
||||
sorted = pos == 0 ? sorted : data[pos - 1] <= data[pos];
|
||||
|
||||
// check if the inserted values are sorted
|
||||
for (int i = 1; i < values.length && sorted; i++) {
|
||||
sorted = data[pos + i - 1] <= data[pos + i];
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,6 +233,11 @@ public final class IntList implements Serializable, Cloneable {
|
||||
}
|
||||
|
||||
data[pos] = value;
|
||||
|
||||
if (sorted) {
|
||||
sorted = pos <= 0 ? sorted : data[pos - 1] <= data[pos];
|
||||
sorted = pos + 1 >= size() ? sorted : data[pos] <= data[pos + 1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,6 +252,13 @@ public final class IntList implements Serializable, Cloneable {
|
||||
ensureCapacity(values.length);
|
||||
|
||||
System.arraycopy(values, 0, data, index, values.length);
|
||||
|
||||
if (sorted) {
|
||||
for (int i = 0; i < values.length && sorted; i++) {
|
||||
sorted = index + i - 1 < 0 ? sorted : data[index + i - 1] <= data[index + i];
|
||||
}
|
||||
}
|
||||
|
||||
index += values.length;
|
||||
}
|
||||
|
||||
@@ -241,6 +295,8 @@ public final class IntList implements Serializable, Cloneable {
|
||||
System.arraycopy(data, toIndex, data, fromIndex, numRemoved);
|
||||
|
||||
index = index - (toIndex - fromIndex);
|
||||
|
||||
sorted = size() <= 1 ? true : sorted; // lists of size 1 or smaller are always sorted
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,6 +324,8 @@ public final class IntList implements Serializable, Cloneable {
|
||||
}
|
||||
}
|
||||
index = insertPosition;
|
||||
|
||||
sorted = size() <= 1 ? true : sorted; // lists of size 1 or smaller are always sorted
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,6 +355,7 @@ public final class IntList implements Serializable, Cloneable {
|
||||
}
|
||||
index = insertPosition;
|
||||
|
||||
sorted = size() <= 1 ? true : sorted; // lists of size 1 or smaller are always sorted
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -334,10 +393,10 @@ public final class IntList implements Serializable, Cloneable {
|
||||
* if the specified {@link UnaryIntOperator} is null
|
||||
*/
|
||||
public void replaceAll(final UnaryIntOperator operator) {
|
||||
final int size = index;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
data[i] = operator.apply(data[i]);
|
||||
for (int i = 0; i < index; i++) {
|
||||
final int newValue = operator.apply(data[i]);
|
||||
set(i, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,6 +477,7 @@ public final class IntList implements Serializable, Cloneable {
|
||||
*/
|
||||
public void sort() {
|
||||
Arrays.sort(data, 0, index);
|
||||
sorted = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -426,6 +486,7 @@ public final class IntList implements Serializable, Cloneable {
|
||||
*/
|
||||
public void parallelSort() {
|
||||
Arrays.parallelSort(data, 0, index);
|
||||
sorted = true;
|
||||
}
|
||||
|
||||
private void ensureCapacity(final int newElements) {
|
||||
|
||||
@@ -66,20 +66,26 @@ public class IntListTest {
|
||||
public void testInsert() {
|
||||
final IntList list = new IntList();
|
||||
|
||||
list.insert(0);
|
||||
Assert.assertArrayEquals(new int[] {}, list.toArray());
|
||||
|
||||
list.insert(0, 1);
|
||||
Assert.assertArrayEquals(new int[] { 1 }, list.toArray());
|
||||
|
||||
list.insert(1, 2);
|
||||
Assert.assertArrayEquals(new int[] { 1, 2 }, list.toArray());
|
||||
list.insert(1, 2, 2, 2);
|
||||
Assert.assertArrayEquals(new int[] { 1, 2, 2, 2 }, list.toArray());
|
||||
|
||||
list.insert(1, 3);
|
||||
Assert.assertArrayEquals(new int[] { 1, 3, 2 }, list.toArray());
|
||||
Assert.assertArrayEquals(new int[] { 1, 3, 2, 2, 2 }, list.toArray());
|
||||
|
||||
list.insert(2, 4, 4, 4);
|
||||
Assert.assertArrayEquals(new int[] { 1, 3, 4, 4, 4, 2 }, list.toArray());
|
||||
Assert.assertArrayEquals(new int[] { 1, 3, 4, 4, 4, 2, 2, 2 }, list.toArray());
|
||||
|
||||
list.insert(6, 5, 5);
|
||||
Assert.assertArrayEquals(new int[] { 1, 3, 4, 4, 4, 2, 5, 5 }, list.toArray());
|
||||
list.insert(2);
|
||||
Assert.assertArrayEquals(new int[] { 1, 3, 4, 4, 4, 2, 2, 2 }, list.toArray());
|
||||
|
||||
list.insert(8, 5, 5);
|
||||
Assert.assertArrayEquals(new int[] { 1, 3, 4, 4, 4, 2, 2, 2, 5, 5 }, list.toArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -108,6 +114,17 @@ public class IntListTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertNull() {
|
||||
final IntList list = new IntList();
|
||||
try {
|
||||
list.insert(0, null);
|
||||
Assert.fail();
|
||||
} catch (final NullPointerException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSet() {
|
||||
final IntList list = new IntList();
|
||||
@@ -530,6 +547,8 @@ public class IntListTest {
|
||||
Assert.assertNotEquals(list, clone);
|
||||
}
|
||||
|
||||
// TODO test clone of empty list
|
||||
|
||||
@Test
|
||||
public void testToString() {
|
||||
Assert.assertEquals("[]", new IntList().toString());
|
||||
@@ -737,4 +756,249 @@ public class IntListTest {
|
||||
list.removeIf(i -> false);
|
||||
Assert.assertArrayEquals(new int[] {}, list.toArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortedFlagSort() {
|
||||
|
||||
Assert.assertTrue("empty list is sorted", new IntList().isSorted());
|
||||
|
||||
final IntList list = new IntList();
|
||||
list.addAll(2, 0, 1);
|
||||
list.sort();
|
||||
Assert.assertTrue("is sorted after calling sort", list.isSorted());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortedFlagEmptyList() {
|
||||
|
||||
Assert.assertTrue("empty list is sorted", new IntList().isSorted());
|
||||
|
||||
final IntList list = new IntList();
|
||||
list.addAll(2, 0, 1);
|
||||
list.remove(0, list.size());
|
||||
Assert.assertTrue("unsorted list initialized by addAll", list.isSorted());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortedFlagAdd() {
|
||||
|
||||
final IntList list = new IntList();
|
||||
list.add(1);
|
||||
Assert.assertTrue("[1]", list.isSorted());
|
||||
list.add(2);
|
||||
Assert.assertTrue("[1,2]", list.isSorted());
|
||||
list.add(2);
|
||||
Assert.assertTrue("[1,2,2]", list.isSorted());
|
||||
list.add(1);
|
||||
Assert.assertFalse("[1,2,2,1]", list.isSorted());
|
||||
list.add(1);
|
||||
Assert.assertFalse("[1,2,2,1,1]", list.isSorted());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortedFlagAddAll() {
|
||||
|
||||
{
|
||||
final IntList list = new IntList();
|
||||
list.addAll(2, 0);
|
||||
Assert.assertFalse("unsorted list initialized by addAll", list.isSorted());
|
||||
}
|
||||
|
||||
{
|
||||
final IntList list = new IntList();
|
||||
list.addAll(1);
|
||||
Assert.assertTrue("list with one element is sorted", list.isSorted());
|
||||
}
|
||||
|
||||
{
|
||||
final IntList list = new IntList();
|
||||
list.addAll(1, 1);
|
||||
Assert.assertTrue("list with all the same elements is sorted", list.isSorted());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortedFlagInsert() {
|
||||
|
||||
/*
|
||||
* tests that result in a sorted list
|
||||
*/
|
||||
{
|
||||
final IntList list = new IntList();
|
||||
list.insert(0, 1, 2, 3);
|
||||
Assert.assertTrue("insert sorted values into empty list", list.isSorted());
|
||||
}
|
||||
|
||||
{
|
||||
final IntList list = IntList.of(1, 1);
|
||||
list.insert(0, 2, 2); // -> [2,2,1,1]
|
||||
Assert.assertFalse("insert before: unsorted", list.isSorted());
|
||||
}
|
||||
{
|
||||
final IntList list = IntList.of(3, 3);
|
||||
list.insert(0, 2, 2); // -> [2,2,3,3]
|
||||
Assert.assertTrue("insert sorted values before: sorted", list.isSorted());
|
||||
}
|
||||
|
||||
{
|
||||
final IntList list = IntList.of(4, 4);
|
||||
list.insert(2, 2, 2); // -> [4,4,2,2]
|
||||
Assert.assertFalse("insert sorted values at end: unsorted", list.isSorted());
|
||||
}
|
||||
{
|
||||
final IntList list = IntList.of(1, 1);
|
||||
list.insert(2, 2, 2); // -> [1,1,2,2]
|
||||
Assert.assertTrue("insert sorted values at end: sorted", list.isSorted());
|
||||
}
|
||||
|
||||
{
|
||||
final IntList list = IntList.of(1, 4);
|
||||
list.insert(1, 2, 2); // -> [1,2,2,4]
|
||||
Assert.assertTrue("insert sorted values in middle: sorted", list.isSorted());
|
||||
}
|
||||
|
||||
/*
|
||||
* tests that result in an unsorted list
|
||||
*/
|
||||
{
|
||||
final IntList list = IntList.of(1, 4);
|
||||
list.insert(1, 5); // -> [1,5,4]
|
||||
Assert.assertFalse("insert sorted values in middle: unsorted", list.isSorted());
|
||||
}
|
||||
{
|
||||
final IntList list = IntList.of(1, 4);
|
||||
list.insert(1, 6, 6); // -> [1,6,6,4]
|
||||
Assert.assertFalse("insert sorted values in middle: unsorted", list.isSorted());
|
||||
}
|
||||
|
||||
{
|
||||
final IntList list = IntList.of(1, 4);
|
||||
list.insert(1, 0); // -> [1,0,4]
|
||||
Assert.assertFalse("insert sorted values in middle: unsorted", list.isSorted());
|
||||
}
|
||||
{
|
||||
final IntList list = IntList.of(1, 4);
|
||||
list.insert(1, 0, 0); // -> [1,0,0,4]
|
||||
Assert.assertFalse("insert sorted values in middle: unsorted", list.isSorted());
|
||||
}
|
||||
|
||||
{
|
||||
final IntList list = IntList.of(3, 4);
|
||||
list.insert(0, 2, 1); // -> [2,1,3,4]
|
||||
Assert.assertFalse("insert unsorted at begin: unsorted", list.isSorted());
|
||||
}
|
||||
{
|
||||
final IntList list = IntList.of(1, 4);
|
||||
list.insert(1, 2, 1); // -> [1,2,1,4]
|
||||
Assert.assertFalse("insert unsorted middle: unsorted", list.isSorted());
|
||||
}
|
||||
{
|
||||
final IntList list = IntList.of(3, 4);
|
||||
list.insert(2, 6, 5); // -> [13,4,6,5]
|
||||
Assert.assertFalse("insert unsorted at end: unsorted", list.isSorted());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortedFlagSetByIndex() {
|
||||
|
||||
{
|
||||
final IntList list = IntList.of(0, 1, 2);
|
||||
list.set(0, -1);
|
||||
Assert.assertTrue("set first element: [-1,1,2] sorted", list.isSorted());
|
||||
list.set(0, 1);
|
||||
Assert.assertTrue("set first element: [1,1,2] sorted", list.isSorted());
|
||||
list.set(0, 2);
|
||||
Assert.assertFalse("set first element: [2,1,2] not sorted", list.isSorted());
|
||||
}
|
||||
|
||||
{
|
||||
final IntList sortedList = IntList.of(0, 2, 4);
|
||||
sortedList.set(1, 3);
|
||||
Assert.assertTrue("set middle element: [0,3,4] sorteed", sortedList.isSorted());
|
||||
sortedList.set(1, 4);
|
||||
Assert.assertTrue("set middle element: [0,4,4] sorteed", sortedList.isSorted());
|
||||
sortedList.set(1, 0);
|
||||
Assert.assertTrue("set middle element: [0,0,4] sorteed", sortedList.isSorted());
|
||||
sortedList.set(1, 5);
|
||||
Assert.assertFalse("set middle element: [0,5,4] not sorteed", sortedList.isSorted());
|
||||
}
|
||||
|
||||
{
|
||||
final IntList sortedList = IntList.of(0, 1, 2);
|
||||
sortedList.set(2, 3);
|
||||
Assert.assertTrue("set last element: [0,1,3] sorteed", sortedList.isSorted());
|
||||
sortedList.set(2, 1);
|
||||
Assert.assertTrue("set last element: [0,1,1] sorteed", sortedList.isSorted());
|
||||
sortedList.set(2, 0);
|
||||
Assert.assertFalse("set last element: [0,1,0] not sorteed", sortedList.isSorted());
|
||||
}
|
||||
|
||||
{
|
||||
final IntList sortedList = IntList.of(0, 1, 2);
|
||||
sortedList.set(2, 0);
|
||||
sortedList.set(2, 2);
|
||||
Assert.assertFalse("unsorted lists stay unsorted", sortedList.isSorted());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortedFlagRemove() {
|
||||
|
||||
final IntList list = IntList.of(4, 3, 2, 1);
|
||||
list.remove(0, 2); // removes 4,3
|
||||
Assert.assertFalse("unsorted list with two elements is not sorted", list.isSorted());
|
||||
|
||||
list.remove(0, 1);
|
||||
Assert.assertTrue("unsorted list with one element becomes sorted", list.isSorted());
|
||||
|
||||
list.add(-1); // make list unsorted again
|
||||
list.remove(0, 2); // remove both elements
|
||||
Assert.assertTrue("unsorted list with no elements becomes sorted", list.isSorted());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortedFlagRemoveAll() {
|
||||
|
||||
final IntList list = IntList.of(4, 3, 2, 1);
|
||||
list.removeAll(IntList.of(4, 3));
|
||||
Assert.assertFalse("unsorted list with two elements is not sorted", list.isSorted());
|
||||
|
||||
list.removeAll(IntList.of(2));
|
||||
Assert.assertTrue("unsorted list with one element becomes sorted", list.isSorted());
|
||||
|
||||
list.add(-1); // make list unsorted again
|
||||
list.removeAll(IntList.of(-1, 2)); // remove both elements
|
||||
Assert.assertTrue("unsorted list with no elements becomes sorted", list.isSorted());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortedFlagRemoveIf() {
|
||||
|
||||
final IntList list = IntList.of(4, 3, 2, 1);
|
||||
list.removeIf(v -> v >= 3); // removes 3 and 4
|
||||
Assert.assertFalse("unsorted list with two elements is not sorted", list.isSorted());
|
||||
|
||||
list.removeIf(v -> v >= 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
|
||||
Assert.assertTrue("unsorted list with no elements becomes sorted", list.isSorted());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortedFlagReplace() {
|
||||
|
||||
final IntList list = IntList.of(1, 2, 3, 4);
|
||||
list.replaceAll(v -> v >= 3 ? 2 : v); // replace 3 and 4 with 2 -> [1,2,2,2]
|
||||
Assert.assertTrue("sorted list still sorted after replace", list.isSorted());
|
||||
|
||||
list.replaceAll(v -> v == 1 ? 3 : v); // replace 1 with 3 -> [3,2,2,2]
|
||||
Assert.assertFalse("sorted list becomes unsorted after replace", list.isSorted());
|
||||
|
||||
list.replaceAll(v -> 2); // replace all with 2 -> [2,2,2,2]
|
||||
Assert.assertFalse("unsorted list stays unsorted", list.isSorted());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user