Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
swim.collections.STreeList Maven / Gradle / Ivy
Go to download
Immutable, structure sharing collections, including hash array mapped tries, finger tries, B-trees, and S-trees (sequence trees)
// Copyright 2015-2019 SWIM.AI inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package swim.collections;
import java.lang.reflect.Array;
import java.util.AbstractList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import swim.codec.Debug;
import swim.codec.Format;
import swim.codec.Output;
import swim.util.Cursor;
import swim.util.KeyedList;
import swim.util.Murmur3;
/**
* Mutable, thread-safe {@link KeyedList} backed by an S-Tree.
*/
public class STreeList extends STreeContext implements KeyedList, Cloneable, Debug {
volatile STreePage root;
protected STreeList(STreePage root) {
this.root = root;
}
public STreeList() {
this(STreePage.empty());
}
@Override
public boolean isEmpty() {
return this.root.isEmpty();
}
@Override
public int size() {
return this.root.size();
}
@Override
public boolean contains(Object value) {
return this.root.contains(value);
}
@Override
public boolean containsAll(Collection> values) {
final STreePage root = this.root;
for (Object value : values) {
if (!root.contains(value)) {
return false;
}
}
return true;
}
@Override
public int indexOf(Object value) {
return this.root.indexOf(value);
}
@Override
public int lastIndexOf(Object value) {
return this.root.lastIndexOf(value);
}
@Override
public T get(int index) {
return get(index, null);
}
@Override
public T get(int index, Object key) {
if (key != null) {
index = lookup(index, key);
if (index < 0) {
return null;
}
}
return this.root.get(index);
}
@Override
public Map.Entry getEntry(int index) {
return getEntry(index, null);
}
@Override
public Map.Entry getEntry(int index, Object key) {
if (key != null) {
index = lookup(index, key);
if (index < 0) {
return null;
}
}
return size() <= index ? null : this.root.getEntry(index);
}
@Override
public T set(int index, T newValue) {
return set(index, newValue, null);
}
@Override
public T set(int index, T newValue, Object key) {
if (key != null) {
index = lookup(index, key);
if (index < 0) {
throw new NoSuchElementException(key.toString());
}
}
do {
final STreePage oldRoot = this.root;
if (index < 0 || index >= oldRoot.size()) {
throw new IndexOutOfBoundsException(Integer.toString(index));
}
final STreePage newRoot = oldRoot.updated(index, newValue, this);
if (oldRoot != newRoot) {
if (ROOT.compareAndSet(this, oldRoot, newRoot)) {
return oldRoot.get(index);
}
} else {
return null;
}
} while (true);
}
@Override
public boolean add(T newValue) {
return add(newValue, null);
}
@Override
public boolean add(T newValue, Object key) {
do {
final STreePage oldRoot = this.root;
final STreePage newRoot = oldRoot.appended(newValue, key, this).balanced(this);
if (ROOT.compareAndSet(this, oldRoot, newRoot)) {
return true;
}
} while (true);
}
@Override
public boolean addAll(Collection extends T> newValues) {
boolean modified = false;
for (T newValue : newValues) {
add(newValue);
modified = true;
}
return modified;
}
@Override
public void add(int index, T newValue) {
add(index, newValue, null);
}
@Override
public void add(int index, T newValue, Object key) {
do {
final STreePage oldRoot = this.root;
if (index < 0 || index > oldRoot.size()) {
throw new IndexOutOfBoundsException(Integer.toString(index));
}
final STreePage newRoot = oldRoot.inserted(index, newValue, key, this).balanced(this);
if (ROOT.compareAndSet(this, oldRoot, newRoot)) {
return;
}
} while (true);
}
@Override
public boolean addAll(int index, Collection extends T> newValues) {
boolean modified = false;
for (T newValue : newValues) {
add(index, newValue);
index += 1;
modified = true;
}
return modified;
}
@Override
public T remove(int index) {
return remove(index, null);
}
@Override
public T remove(int index, Object key) {
if (key != null) {
index = lookup(index, key);
if (index < 0) {
return null;
}
}
do {
final STreePage oldRoot = this.root;
if (index < 0 || index > oldRoot.size()) {
return null;
}
final STreePage newRoot = oldRoot.removed(index, this);
if (oldRoot != newRoot) {
if (ROOT.compareAndSet(this, oldRoot, newRoot)) {
return oldRoot.get(index);
}
} else {
return null;
}
} while (true);
}
@Override
public boolean remove(Object value) {
do {
final STreePage oldRoot = this.root;
final STreePage newRoot = oldRoot.removed(value, this);
if (oldRoot != newRoot) {
if (ROOT.compareAndSet(this, oldRoot, newRoot)) {
return true;
}
} else {
return false;
}
} while (true);
}
@Override
public boolean removeAll(Collection> values) {
do {
final STreePage oldRoot = this.root;
STreePage newRoot = oldRoot;
int n = newRoot.size();
int i = 0;
while (i < n) {
final T value = newRoot.get(i);
if (values.contains(value)) {
newRoot = newRoot.removed(i, this);
n -= 1;
} else {
i += 1;
}
}
if (oldRoot != newRoot) {
if (ROOT.compareAndSet(this, oldRoot, newRoot)) {
return true;
}
} else {
return false;
}
} while (true);
}
@Override
public boolean retainAll(Collection> values) {
do {
final STreePage oldRoot = this.root;
STreePage newRoot = oldRoot;
int n = newRoot.size();
int i = 0;
while (i < n) {
final T value = newRoot.get(i);
if (!values.contains(value)) {
newRoot = newRoot.removed(i, this);
n -= 1;
} else {
i += 1;
}
}
if (oldRoot != newRoot) {
if (ROOT.compareAndSet(this, oldRoot, newRoot)) {
return true;
}
} else {
return false;
}
} while (true);
}
@Override
public void move(int fromIndex, int toIndex) {
move(fromIndex, toIndex, null);
}
@Override
public void move(int fromIndex, int toIndex, Object key) {
if (key != null) {
fromIndex = lookup(fromIndex, key);
if (fromIndex < 0) {
throw new NoSuchElementException(key.toString());
}
}
do {
final STreePage oldRoot = this.root;
if (fromIndex < 0 || fromIndex >= oldRoot.size()) {
throw new IndexOutOfBoundsException(Integer.toString(fromIndex));
}
if (toIndex < 0 || toIndex >= oldRoot.size()) {
throw new IndexOutOfBoundsException(Integer.toString(toIndex));
}
if (fromIndex != toIndex) {
final Map.Entry entry = oldRoot.getEntry(fromIndex);
final STreePage newRoot = oldRoot.removed(fromIndex, this)
.inserted(toIndex, entry.getValue(), entry.getKey(), this)
.balanced(this);
if (ROOT.compareAndSet(this, oldRoot, newRoot)) {
break;
}
} else {
break;
}
} while (true);
}
public void drop(int lower) {
do {
final STreePage oldRoot = this.root;
if (lower > 0 && oldRoot.size() > 0) {
final STreePage newRoot;
if (lower < oldRoot.size()) {
newRoot = oldRoot.drop(lower, this);
} else {
newRoot = STreePage.empty();
}
if (ROOT.compareAndSet(this, oldRoot, newRoot)) {
break;
}
} else {
break;
}
} while (true);
}
public void take(int keep) {
do {
final STreePage oldRoot = this.root;
if (keep < oldRoot.size() && oldRoot.size() > 0) {
final STreePage newRoot;
if (keep > 0) {
newRoot = oldRoot.take(keep, this);
} else {
newRoot = STreePage.empty();
}
if (ROOT.compareAndSet(this, oldRoot, newRoot)) {
break;
}
} else {
break;
}
} while (true);
}
public void clear() {
STreePage oldRoot;
do {
oldRoot = this.root;
} while (!ROOT.compareAndSet(this, oldRoot, STreePage.empty()));
}
@Override
public Object[] toArray() {
final STreePage root = this.root;
final int n = root.size();
final Object[] array = new Object[n];
root.copyToArray(array, 0);
return array;
}
@SuppressWarnings("unchecked")
@Override
public U[] toArray(U[] array) {
final STreePage root = this.root;
final int n = root.size();
if (array.length < n) {
array = (U[]) Array.newInstance(array.getClass().getComponentType(), n);
}
root.copyToArray(array, 0);
if (array.length > n) {
array[n] = null;
}
return array;
}
@Override
public Cursor iterator() {
return this.root.iterator();
}
@Override
public Cursor listIterator() {
return this.root.iterator();
}
@Override
public Cursor listIterator(int index) {
final Cursor cursor = listIterator();
cursor.skip(index);
return cursor;
}
@Override
public Cursor keyIterator() {
return this.root.keyIterator();
}
@Override
public Cursor> entryIterator() {
return this.root.entryIterator();
}
public Cursor reverseIterator() {
return this.root.reverseIterator();
}
public Cursor reverseKeyIterator() {
return this.root.reverseKeyIterator();
}
public Cursor> reverseEntryIterator() {
return this.root.reverseEntryIterator();
}
public STree snapshot() {
return new STree(this.root);
}
@Override
public List subList(int fromIndex, int toIndex) {
if (fromIndex > toIndex) {
throw new IllegalArgumentException();
}
return new STreeListSubList(this, fromIndex, toIndex);
}
public STree clone() {
return copy(this.root);
}
protected STree copy(STreePage root) {
return new STree(root);
}
protected int lookup(int start, Object key) {
final STreePage root = this.root;
start = Math.min(Math.max(0, start), root.size() - 1);
if (start > -1) { // when root.size() is 0
int index = start;
do {
final Map.Entry entry = root.getEntry(index);
if (entry != null && compare(entry.getKey(), key) == 0) {
return index;
}
index = (index + 1) % root.size();
} while (index != start);
}
return -1;
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (other instanceof STree>) {
final STree that = (STree) other;
if (this.size() == that.size()) {
final Cursor these = iterator();
final Cursor those = that.iterator();
while (these.hasNext() && those.hasNext()) {
final T x = these.next();
final T y = those.next();
if (x == null ? y != null : !x.equals(y)) {
return false;
}
}
return true;
}
}
return false;
}
@Override
public int hashCode() {
if (hashSeed == 0) {
hashSeed = Murmur3.seed(STree.class);
}
int h = hashSeed;
final Cursor these = iterator();
while (these.hasNext()) {
h = Murmur3.mix(h, Murmur3.hash(these.next()));
}
return Murmur3.mash(h);
}
@Override
public void debug(Output> output) {
output = output.write("STreeList").write('.');
final Cursor these = iterator();
if (these.hasNext()) {
output = output.write("of").write('(').debug(these.next());
while (these.hasNext()) {
output = output.write(", ").debug(these.next());
}
} else {
output = output.write("empty").write('(');
}
output = output.write(')');
}
@Override
public String toString() {
return Format.debug(this);
}
private static int hashSeed;
public static STreeList empty() {
return new STreeList();
}
@SuppressWarnings("unchecked")
public static STree of(T... values) {
final STree tree = new STree();
for (T value : values) {
tree.add(value);
}
return tree;
}
@SuppressWarnings("rawtypes")
static final AtomicReferenceFieldUpdater ROOT =
AtomicReferenceFieldUpdater.newUpdater(STreeList.class, STreePage.class, "root");
}
final class STreeListSubList extends AbstractList {
final STreeList inner;
final int fromIndex;
final int toIndex;
STreeListSubList(STreeList inner, int fromIndex, int toIndex) {
this.inner = inner;
this.fromIndex = fromIndex;
this.toIndex = toIndex;
}
@Override
public int size() {
return this.toIndex - this.fromIndex;
}
@Override
public T get(int index) {
final int i = this.fromIndex + index;
if (i < this.fromIndex || i >= this.toIndex) {
throw new IndexOutOfBoundsException(Integer.toString(index));
}
return this.inner.get(i);
}
@Override
public List subList(int fromIndex, int toIndex) {
if (fromIndex > toIndex) {
throw new IllegalArgumentException();
}
fromIndex += this.fromIndex;
toIndex += this.fromIndex;
if (toIndex > this.toIndex) {
throw new IndexOutOfBoundsException();
}
return new STreeListSubList(this.inner, fromIndex, toIndex);
}
}