io.lacuna.bifurcan.LinearList Maven / Gradle / Ivy
package io.lacuna.bifurcan;
import io.lacuna.bifurcan.utils.Bits;
import java.util.*;
import static io.lacuna.bifurcan.utils.Bits.log2Ceil;
/**
* A simple implementation of a mutable list combining the best characteristics of {@code java.util.ArrayList} and
* {@code java.util.ArrayDeque}, allowing elements to be added and removed from both ends of the collection and
* allowing random-access reads and updates.
*
* Calls to {@code concat()}, {@code slice()}, and {@code split()} create virtual collections which retain a reference
* to the whole underlying collection, and are somewhat less efficient than {@code LinearList}.
*
* @author ztellman
*/
@SuppressWarnings("unchecked")
public class LinearList implements IList, Cloneable {
private static final int DEFAULT_CAPACITY = 4;
private Object[] elements;
private int mask;
private int size, offset;
private int hash = -1;
public static LinearList of(V... elements) {
LinearList list = new LinearList(elements.length);
for (V e : elements) {
list.addLast(e);
}
return list;
}
/**
* @param collection a {@code java.util.Collection}
* @return a list containing the entries of the collection
*/
public static LinearList from(Collection collection) {
return collection.stream().collect(Lists.linearCollector(collection.size()));
}
/**
* @param iterable an {@code Iterable} object
* @return a list containing the elements of the iterator
*/
public static LinearList from(Iterable iterable) {
return from(iterable.iterator());
}
/**
* @param iterator an {@code java.util.Iterator}
* @return a list containing all remaining elements of the iterator
*/
public static LinearList from(Iterator iterator) {
LinearList list = new LinearList();
iterator.forEachRemaining(list::addLast);
return list;
}
/**
* @param list another list
* @return a {@code LinearList} containing all the elements of the list
*/
public static LinearList from(IList list) {
if (list.size() > Integer.MAX_VALUE) {
throw new IllegalArgumentException("LinearList cannot hold more than 1 << 30 entries");
} else if (list instanceof LinearList) {
return ((LinearList) list).clone();
} else {
return list.stream().collect(Lists.linearCollector((int) list.size()));
}
}
/**
* Creates a new {@code LinearList}.
*/
public LinearList() {
this(DEFAULT_CAPACITY);
}
/**
* Creates a new {@code LinearList}.
*
* @param capacity the initial capacity of the list
*/
public LinearList(int capacity) {
this(0, new Object[Math.max(1, 1 << log2Ceil(capacity))]);
}
private LinearList(int size, Object[] elements) {
this.size = size;
this.offset = 0;
this.mask = elements.length - 1;
this.elements = elements;
}
///
private void resize(int newCapacity) {
Object[] nElements = new Object[newCapacity];
int truncatedSize = Math.min(size, elements.length - offset);
System.arraycopy(elements, offset, nElements, 0, truncatedSize);
if (size != truncatedSize) {
System.arraycopy(elements, 0, nElements, truncatedSize, size - truncatedSize);
}
mask = nElements.length - 1;
elements = nElements;
offset = 0;
}
@Override
public boolean isLinear() {
return true;
}
@Override
public LinearList addLast(V value) {
if (size == elements.length) {
resize(size << 1);
}
elements[(offset + size++) & mask] = value;
hash = -1;
return this;
}
@Override
public LinearList addFirst(V value) {
if (size == elements.length) {
resize(size << 1);
}
offset = (offset - 1) & mask;
elements[offset] = value;
size++;
hash = -1;
return this;
}
@Override
public LinearList removeFirst() {
if (size == 0) {
return this;
}
offset = (offset + 1) & mask;
size--;
hash = -1;
return this;
}
@Override
public LinearList removeLast() {
if (size == 0) {
return this;
}
elements[(offset + --size) & mask] = null;
hash = -1;
return this;
}
public LinearList clear() {
Arrays.fill(elements, null);
offset = 0;
size = 0;
hash = -1;
return this;
}
@Override
public LinearList set(long idx, V value) {
if (idx == size) {
return addLast(value);
} else if (idx > Integer.MAX_VALUE) {
throw new IndexOutOfBoundsException();
}
elements[(int) (offset + (int) idx) & mask] = value;
hash = -1;
return this;
}
LinearList linearConcat(IList l) {
long newSize = size() + l.size();
if (newSize > Integer.MAX_VALUE) {
throw new IllegalArgumentException("cannot hold more than 1 << 31 entries");
}
if (l instanceof LinearList) {
LinearList list = (LinearList) l;
if (offset != 0 || newSize > elements.length) {
resize(1 << log2Ceil(newSize));
}
int truncatedListSize = Math.min(list.size, list.elements.length - list.offset);
System.arraycopy(list.elements, list.offset, elements, size, truncatedListSize);
if (list.size != truncatedListSize) {
System.arraycopy(list.elements, 0, elements, size + truncatedListSize, list.size - truncatedListSize);
}
size += list.size();
hash = -1;
} else {
for (V e : l) {
addLast(e);
}
}
return this;
}
@Override
public V nth(long idx) {
if (idx < 0 || idx >= size) {
throw new IndexOutOfBoundsException(idx + " must be within [0," + size + ")");
}
return (V) elements[(offset + (int) idx) & mask];
}
/**
* Removes, and returns, the first element of the list.
*
* @return the first element of the list
* @throws IndexOutOfBoundsException if the list is empty
*/
public V popFirst() {
V val = first();
removeFirst();
return val;
}
/**
* Removes, and returns, the last element of the list.
*
* @return the last element of the list
* @throws IndexOutOfBoundsException if the list is empty
*/
public V popLast() {
V val = last();
removeLast();
return val;
}
@Override
public Iterator iterator() {
return new Iterator() {
final int limit = offset + size;
int idx = offset;
@Override
public boolean hasNext() {
return idx != limit;
}
@Override
public V next() {
if (idx == limit) {
throw new NoSuchElementException();
}
V val = (V) elements[idx++ & mask];
return val;
}
};
}
@Override
public long size() {
return size;
}
@Override
public IList forked() {
return new Lists.VirtualList<>(this);
}
@Override
public IList linear() {
return this;
}
@Override
public int hashCode() {
if (hash == -1) {
hash = (int) Lists.hash(this);
}
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof IList) {
return Lists.equals(this, (IList) obj);
}
return false;
}
@Override
public String toString() {
return Lists.toString(this);
}
@Override
public LinearList clone() {
LinearList l = new LinearList(size, elements.clone());
l.offset = offset;
return l;
}
}