org.jhotdraw8.icollection.VectorList Maven / Gradle / Ivy
/*
* @(#)SimpleImmutableList.java
* Copyright © 2023 The authors and contributors of JHotDraw. MIT License.
*/
package org.jhotdraw8.icollection;
import org.jhotdraw8.icollection.facade.ReadOnlyListFacade;
import org.jhotdraw8.icollection.immutable.ImmutableList;
import org.jhotdraw8.icollection.impl.vector.BitMappedTrie;
import org.jhotdraw8.icollection.readonly.ReadOnlyCollection;
import org.jhotdraw8.icollection.readonly.ReadOnlyList;
import org.jhotdraw8.icollection.readonly.ReadOnlySequencedCollection;
import org.jhotdraw8.icollection.serialization.ListSerializationProxy;
import org.jspecify.annotations.Nullable;
import java.io.Serial;
import java.io.Serializable;
import java.util.AbstractList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Spliterator;
import java.util.stream.Stream;
/**
* Implements the {@link ImmutableList} interface using a bit-mapped trie
* (Vector).
*
* The code has been derived from Vavr Vector.java.
*
* Features:
*
* - supports up to 231 - 1 elements
* - allows null elements
* - is immutable
* - is thread-safe
* - iterates in the order of the list
*
*
* Performance characteristics:
*
* - addLast: O(log₃₂ N)
* - set: O(log₃₂ N)
* - removeAt: O(N)
* - removeFirst,removeLast: O(log₃₂ N)
* - contains: O(N)
* - toMutable: O(1)
* - clone: O(1)
* - iterator creation: O(log₃₂ N)
* - iterator.next: O(1)
* - getFirst, getLast: O(log₃₂ N)
* - reversed: O(N)
*
*
* References:
*
* For a similar design, see 'Vector.java' in vavr. The internal data structure of
* this class is licensed from vavr.
*
* - Vector.java. Copyright 2023 (c) vavr. MIT License.
* - github.com
*
*
* @param the element type
*/
public class VectorList implements ImmutableList, Serializable {
@Serial
private static final long serialVersionUID = 0L;
private static final VectorList> EMPTY = new VectorList<>();
final transient BitMappedTrie trie;
/**
* Constructs a new empty list.
*/
protected VectorList() {
this.trie = BitMappedTrie.empty();
}
/**
* Constructs a new list that contains all the elements of
* the specified iterable.
*
* @param iterable an iterable
*/
@SuppressWarnings("unchecked")
protected VectorList(final @Nullable Iterable extends E> iterable) {
if (iterable == null) {
this.trie = BitMappedTrie.empty();
} else if (iterable instanceof Collection> c && c.isEmpty()
|| iterable instanceof ReadOnlyCollection> rc && rc.isEmpty()) {
this.trie = BitMappedTrie.empty();
} else if (iterable instanceof VectorList extends E> that) {
this.trie = (BitMappedTrie) that.trie;
} else if (iterable instanceof MutableVectorList extends E> mc) {
VectorList extends E> that = mc.toImmutable();
this.trie = (BitMappedTrie) that.trie;
} else if (iterable instanceof Collection> c) {
this.trie = BitMappedTrie.ofAll(c.toArray());
} else {
BitMappedTrie root = BitMappedTrie.empty().appendAll(iterable);
this.trie = root.length() == 0 ? BitMappedTrie.empty() : root;
}
}
VectorList(BitMappedTrie trie) {
this.trie = trie;
}
/**
* Creates a new instance with the provided privateData data object.
*
* This constructor is intended to be called from a constructor
* of the subclass, that is called from method {@link #newInstance(PrivateData)}.
*
* @param privateData an privateData data object
*/
protected VectorList(PrivateData privateData) {
this.trie = privateData.get();
}
/**
* Creates a new instance with the provided privateData object as its internal data structure.
*
* Subclasses must override this method, and return a new instance of their subclass!
*
* @param privateData the internal data structure needed by this class for creating the instance.
* @return a new instance of the subclass
*/
protected VectorList newInstance(PrivateData privateData) {
return new VectorList<>(privateData);
}
@SuppressWarnings("unchecked")
private VectorList newInstance(BitMappedTrie trie) {
return newInstance(new PrivateData(trie));
}
@SuppressWarnings("unchecked")
public static VectorList of() {
return (VectorList) EMPTY;
}
@SafeVarargs
@SuppressWarnings("varargs")
public static VectorList of(T... t) {
return new VectorList<>(BitMappedTrie.ofAll(t));
}
public static VectorList ofIterator(Iterator iterator) {
return VectorList.of().addAll(() -> iterator);
}
public static VectorList ofStream(Stream stream) {
return VectorList.of().addAll(stream::iterator);
}
@SuppressWarnings("unchecked")
public static VectorList copyOf(Iterable extends T> iterable) {
Objects.requireNonNull(iterable, "iterable is null");
if (iterable instanceof Collection> c && c.isEmpty()
|| iterable instanceof ReadOnlyCollection> rc && rc.isEmpty()) {
return of();
}
if (iterable instanceof VectorList) {
return (VectorList) iterable;
}
if (iterable instanceof MutableVectorList> mc) {
return (VectorList) mc.toImmutable();
}
if (iterable instanceof Collection> c) {
return new VectorList<>(BitMappedTrie.ofAll(c.toArray()));
}
BitMappedTrie root = BitMappedTrie.empty().appendAll(iterable);
return root.length() == 0 ? of() : new VectorList<>(root);
}
@Override
public VectorList empty() {
return of();
}
@Override
public VectorList add(E element) {
return newInstance(trie.append(element));
}
@Override
public VectorList add(int index, E element) {
if (index == 0) {
return newInstance(trie.prepend(element));
}
return index == size() ? add(element) : addAll(index, Collections.singleton(element));
}
@Override
public VectorList addAll(Iterable extends E> c) {
Objects.requireNonNull(c, "iterable is null");
if (isEmpty()) {
return copyOf(c);
}
int cSize = c instanceof Collection> cc ? cc.size() :
c instanceof ReadOnlyCollection> rcc ? rcc.size() : -1;
if (cSize == 0) {
return this;
}
if (cSize < 0) {
BitMappedTrie newRoot = this.trie;
int newSize = size();
for (E e : c) {
newRoot = newRoot.append(e);
newSize++;
}
return newInstance(newRoot);
}
return newInstance(trie.appendAll(c));
}
@Override
public VectorList addFirst(@Nullable E element) {
return add(0, element);
}
@Override
public VectorList addLast(@Nullable E element) {
return newInstance(trie.append(element));
}
@Override
public VectorList addAll(int index, Iterable extends E> c) {
Objects.requireNonNull(c, "c is null");
if (index >= 0 && index <= size()) {
final VectorList begin = readOnlySubList(0, index).addAll(c);
final VectorList end = readOnlySubList(index, size());
return begin.addAll(end);
} else {
throw new IndexOutOfBoundsException("addAll(" + index + ", c) on Vector of size " + size());
}
}
@Override
public ReadOnlySequencedCollection readOnlyReversed() {
return new ReadOnlyListFacade<>(
this::size,
index -> get(size() - 1 - index),
() -> this);
}
public VectorList reverse() {
return size() < 2 ? this : VectorList.copyOf(readOnlyReversed());
}
@Override
public VectorList remove(E element) {
int index = indexOf(element);
return index < 0 ? this : removeAt(index);
}
@Override
public VectorList removeAt(int index) {
return removeRange(index, index + 1);
}
@Override
public VectorList removeFirst() {
return (VectorList) ImmutableList.super.removeFirst();
}
@Override
public VectorList removeLast() {
return (VectorList) ImmutableList.super.removeLast();
}
@SuppressWarnings("unchecked")
@Override
public VectorList retainAll(Iterable> c) {
if (isEmpty()) {
return this;
}
final Collection set;
if (c instanceof Collection> cc) {
set = (Collection) cc;
} else if (c instanceof ReadOnlyCollection> rc) {
set = (Collection) rc.asCollection();
} else {
set = new HashSet<>();
c.forEach(e -> set.add((E) e));
}
if (set.isEmpty()) {
return of();
}
var t = this.toMutable();
boolean modified = false;
for (E key : this) {
if (!set.contains(key)) {
t.remove(key);
modified = true;
}
}
return modified ? t.toImmutable() : this;
}
@Override
public VectorList removeRange(int fromIndex, int toIndex) {
Objects.checkIndex(fromIndex, toIndex + 1);
Objects.checkIndex(toIndex, size() + 1);
var begin = trie.take(fromIndex);
var end = trie.drop(toIndex);
return newInstance(begin.append(end.iterator(), end.length));
// The following code does not work as expected, because prepend inserts
// elements in reverse sequence.
/*
return newInstance(begin.length > end.length
? begin.append(end.iterator(), end.length)
: end.prepend(begin.iterator(), begin.length),
size - (toIndex - fromIndex));
*/
}
@Override
public VectorList removeAll(Iterable> c) {
if (isEmpty()) {
return this;
}
VectorList result = this;
Outer:
for (Object e : c) {
for (int index = result.indexOf(e); index >= 0; index = result.indexOf(e, index)) {
result = result.removeAt(index);
if (result.isEmpty()) {
break Outer;
}
}
}
return result;
}
@Override
public VectorList set(int index, E element) {
BitMappedTrie newRoot = trie.update(index, element);
return newRoot == this.trie ? this : newInstance(newRoot);
}
@Override
public E get(int index) {
Objects.checkIndex(index, size());
return trie.get(index);
}
@Override
public VectorList readOnlySubList(int fromIndex, int toIndex) {
Objects.checkIndex(fromIndex, toIndex + 1);
Objects.checkIndex(toIndex, size() + 1);
BitMappedTrie newRoot = this.trie;
if (toIndex < size()) {
newRoot = newRoot.take(toIndex);
}
if (fromIndex > 0) {
newRoot = newRoot.drop(fromIndex);
}
return newRoot == this.trie ? this : newInstance(newRoot);
}
@Override
public int size() {
return trie.length;
}
public int indexOf(Object o, int fromIndex) {
if (fromIndex < size()) {
for (Iterator i = trie.iterator(fromIndex, size()); i.hasNext(); fromIndex++) {
E e = i.next();
if (Objects.equals(o, e)) {
return fromIndex;
}
}
}
return -1;
}
@Override
public boolean contains(Object o) {
for (E e : this) {
if (Objects.equals(e, o)) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return ReadOnlyList.iteratorToHashCode(iterator());
}
@Override
public MutableVectorList toMutable() {
return new MutableVectorList<>(this);
}
@Serial
private Object writeReplace() {
return new VectorList.SerializationProxy<>(this.toMutable());
}
@Override
public Iterator iterator() {
return trie.iterator(0, size());
}
@Override
public int maxSize() {
return Integer.MAX_VALUE;
}
@Override
public Spliterator spliterator() {
return trie.spliterator(0, size(), Spliterator.SIZED | Spliterator.ORDERED | Spliterator.SUBSIZED);
}
@Override
public boolean equals(Object obj) {
return ReadOnlyList.listEquals(this, obj);
}
/**
* Returns a string representation of this list.
*
* The string representation is consistent with the one produced
* by {@link AbstractList#toString()}.
*
* @return a string representation
*/
@Override
public String toString() {
return ReadOnlyCollection.iterableToString(this);
}
private static class SerializationProxy extends ListSerializationProxy {
@Serial
private static final long serialVersionUID = 0L;
protected SerializationProxy(List target) {
super(target);
}
@Serial
@Override
protected Object readResolve() {
return VectorList.of().addAll(deserializedElements);
}
}
}