提交 79f90fe3 authored 作者: Thomas Mueller's avatar Thomas Mueller

Immutable array (work in progress)

上级 8c93fb4d
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.store;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
import org.h2.dev.util.ImmutableArray2;
import org.h2.test.TestBase;
/**
* Test the concurrent linked list.
*/
public class TestImmutableArray extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestImmutableArray test = (TestImmutableArray) TestBase.createCaller().init();
test.test();
testPerformance();
}
@Override
public void test() throws Exception {
testRandomized();
}
private static void testPerformance() {
testPerformance(true);
testPerformance(false);
testPerformance(true);
testPerformance(false);
testPerformance(true);
testPerformance(false);
}
private static void testPerformance(final boolean immutable) {
// immutable time 2068 dummy: 60000000
// immutable time 1140 dummy: 60000000
// ArrayList time 361 dummy: 60000000
System.out.print(immutable ? "immutable" : "ArrayList");
long start = System.currentTimeMillis();
int count = 20000000;
Integer x = 1;
int sum = 0;
if (immutable) {
ImmutableArray2<Integer> test = ImmutableArray2.empty();
for (int i = 0; i < count; i++) {
if (i % 10 != 0) {
test = test.insert(test.length(), x);
} else {
test = test.insert(i % 30, x);
}
if (test.length() > 100) {
while (test.length() > 30) {
if (i % 10 != 0) {
test = test.remove(test.length() - 1);
} else {
test = test.remove(0);
}
}
}
sum += test.get(0);
sum += test.get(test.length() - 1);
sum += test.get(test.length() / 2);
if (i % 10 == 0) {
test = test.set(0, x);
}
}
} else {
ArrayList<Integer> test = new ArrayList<Integer>();
for (int i = 0; i < count; i++) {
if (i % 10 != 0) {
test.add(test.size(), x);
} else {
test.add(i % 30, x);
}
if (test.size() > 100) {
while (test.size() > 30) {
if (i % 2 == 0) {
test.remove(test.size() - 1);
} else {
test.remove(0);
}
}
}
sum += test.get(0);
sum += test.get(test.size() - 1);
sum += test.get(test.size() / 2);
if (i % 10 == 0) {
test.set(0, x);
}
}
}
System.out.println(" time " + (System.currentTimeMillis() - start) +
" dummy: " + sum);
}
private void testRandomized() {
Random r = new Random(0);
for (int i = 0; i < 100; i++) {
ImmutableArray2<Integer> test = ImmutableArray2.empty();
// ConcurrentRing<Integer> test = new ConcurrentRing<Integer>();
ArrayList<Integer> x = new ArrayList<Integer>();
StringBuilder buff = new StringBuilder();
for (int j = 0; j < 1000; j++) {
buff.append("[" + j + "] ");
int opType = r.nextInt(3);
switch (opType) {
case 0: {
int index = test.length() == 0 ? 0 : r.nextInt(test.length());
int value = r.nextInt(100);
buff.append("insert " + index + " " + value + "\n");
test = test.insert(index, value);
x.add(index, value);
break;
}
case 1: {
if (test.length() > 0) {
int index = r.nextInt(test.length());
int value = r.nextInt(100);
buff.append("set " + index + " " + value + "\n");
x.set(index, value);
test = test.set(index, value);
}
break;
}
case 2: {
if (test.length() > 0) {
int index = r.nextInt(test.length());
buff.append("remove " + index + "\n");
x.remove(index);
test = test.remove(index);
}
break;
}
}
assertEquals(x.size(), test.length());
assertEquals(toString(x.iterator()), toString(test.iterator()));
}
}
}
private static <T> String toString(Iterator<T> it) {
StringBuilder buff = new StringBuilder();
while (it.hasNext()) {
buff.append(' ').append(it.next());
}
return buff.toString();
}
}
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.dev.util;
import java.util.Arrays;
import java.util.Iterator;
import org.h2.mvstore.DataUtils;
/**
* An immutable array.
*
* @param <K> the type
*/
public final class ImmutableArray<K> implements Iterable<K> {
private static final ImmutableArray<?> EMPTY = new ImmutableArray<Object>(
new Object[0]);
/**
* The array.
*/
private final K[] array;
private ImmutableArray(K[] array) {
this.array = array;
}
/**
* Get the entry at this index.
*
* @param index the index
* @return the entry
*/
public K get(int index) {
return array[index];
}
/**
* Get the length.
*
* @return the length
*/
public int length() {
return array.length;
}
/**
* Set the entry at this index.
*
* @param index the index
* @param obj the object
* @return the new immutable array
*/
public ImmutableArray<K> set(int index, K obj) {
K[] array = Arrays.copyOf(this.array, this.array.length);
array[index] = obj;
return new ImmutableArray<K>(array);
}
/**
* Insert an entry at this index.
*
* @param index the index
* @param obj the object
* @return the new immutable array
*/
public ImmutableArray<K> insert(int index, K obj) {
int len = array.length + 1;
@SuppressWarnings("unchecked")
K[] array = (K[]) new Object[len];
DataUtils.copyWithGap(this.array, array, this.array.length, index);
array[index] = obj;
return new ImmutableArray<K>(array);
}
/**
* Remove the entry at this index.
*
* @param index the index
* @return the new immutable array
*/
public ImmutableArray<K> remove(int index) {
int len = array.length - 1;
@SuppressWarnings("unchecked")
K[] array = (K[]) new Object[len];
DataUtils.copyExcept(this.array, array, this.array.length, index);
return new ImmutableArray<K>(array);
}
/**
* Get a sub-array.
*
* @param fromIndex the index of the first entry
* @param toIndex the end index, plus one
* @return the new immutable array
*/
public ImmutableArray<K> subArray(int fromIndex, int toIndex) {
int len = toIndex - fromIndex;
@SuppressWarnings("unchecked")
K[] array = (K[]) new Object[len];
System.arraycopy(this.array, fromIndex, array, 0, toIndex - fromIndex);
return new ImmutableArray<K>(array);
}
/**
* Create an immutable array.
*
* @param array the data
* @return the new immutable array
*/
public static <K> ImmutableArray<K> create(K... array) {
return new ImmutableArray<K>(array);
}
/**
* Get the data.
*
* @return the data
*/
public K[] array() {
return array;
}
@Override
public String toString() {
StringBuilder buff = new StringBuilder();
for (K obj : this) {
buff.append(' ').append(obj);
}
return buff.toString();
}
/**
* Get an empty immutable array.
*
* @param <K> the key type
* @return the array
*/
@SuppressWarnings("unchecked")
public static <K> ImmutableArray<K> empty() {
return (ImmutableArray<K>) EMPTY;
}
/**
* Get an iterator over all entries.
*
* @return the iterator
*/
@Override
public Iterator<K> iterator() {
return new Iterator<K>() {
ImmutableArray<K> a = ImmutableArray.this;
int index;
@Override
public boolean hasNext() {
return index < a.length();
}
@Override
public K next() {
return a.get(index++);
}
@Override
public void remove() {
throw DataUtils.newUnsupportedOperationException("remove");
}
};
}
}
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.dev.util;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicBoolean;
import org.h2.mvstore.DataUtils;
/**
* An immutable array.
*
* @param <K> the type
*/
public final class ImmutableArray2<K> implements Iterable<K> {
private static final ImmutableArray2<?> EMPTY = new ImmutableArray2<Object>(
new Object[0], 0);
/**
* The array.
*/
private final K[] array;
private final int length;
private AtomicBoolean canExtend;
private ImmutableArray2(K[] array, int len) {
this.array = array;
this.length = len;
}
private ImmutableArray2(K[] array, int len, boolean canExtend) {
this.array = array;
this.length = len;
if (canExtend) {
this.canExtend = new AtomicBoolean(true);
}
}
/**
* Get the entry at this index.
*
* @param index the index
* @return the entry
*/
public K get(int index) {
if (index >= length) {
throw new IndexOutOfBoundsException();
}
return array[index];
}
/**
* Get the length.
*
* @return the length
*/
public int length() {
return length;
}
/**
* Set the entry at this index.
*
* @param index the index
* @param obj the object
* @return the new immutable array
*/
public ImmutableArray2<K> set(int index, K obj) {
K[] a2 = Arrays.copyOf(array, length);
a2[index] = obj;
return new ImmutableArray2<K>(a2, length);
}
/**
* Insert an entry at this index.
*
* @param index the index
* @param obj the object
* @return the new immutable array
*/
public ImmutableArray2<K> insert(int index, K obj) {
int len = length + 1;
int newLen = len;
boolean extendable;
if (index == len - 1) {
AtomicBoolean x = canExtend;
// can set it to null early - we anyway
// reset the flag, so it is no longer useful
canExtend = null;
if (array.length > index && x != null) {
if (x.getAndSet(false)) {
array[index] = obj;
return new ImmutableArray2<K>(array, len, true);
}
}
extendable = true;
newLen = len + 4;
} else {
extendable = false;
}
@SuppressWarnings("unchecked")
K[] a2 = (K[]) new Object[newLen];
DataUtils.copyWithGap(array, a2, length, index);
a2[index] = obj;
return new ImmutableArray2<K>(a2, len, extendable);
}
/**
* Remove the entry at this index.
*
* @param index the index
* @return the new immutable array
*/
public ImmutableArray2<K> remove(int index) {
int len = length - 1;
if (index == len) {
return new ImmutableArray2<K>(array, len);
}
@SuppressWarnings("unchecked")
K[] a2 = (K[]) new Object[len];
DataUtils.copyExcept(array, a2, length, index);
return new ImmutableArray2<K>(a2, len);
}
/**
* Get a sub-array.
*
* @param fromIndex the index of the first entry
* @param toIndex the end index, plus one
* @return the new immutable array
*/
public ImmutableArray2<K> subArray(int fromIndex, int toIndex) {
int len = toIndex - fromIndex;
if (fromIndex == 0) {
return new ImmutableArray2<K>(array, len);
}
@SuppressWarnings("unchecked")
K[] a2 = (K[]) new Object[len];
System.arraycopy(array, fromIndex, a2, 0, toIndex - fromIndex);
return new ImmutableArray2<K>(a2, len);
}
/**
* Create an immutable array.
*
* @param array the data
* @return the new immutable array
*/
public static <K> ImmutableArray2<K> create(K... array) {
return new ImmutableArray2<K>(array, array.length);
}
/**
* Get the data.
*
* @return the data
*/
public K[] array() {
return array;
}
@Override
public String toString() {
StringBuilder buff = new StringBuilder();
for (K obj : this) {
buff.append(' ').append(obj);
}
return buff.toString();
}
/**
* Get an empty immutable array.
*
* @param <K> the key type
* @return the array
*/
@SuppressWarnings("unchecked")
public static <K> ImmutableArray2<K> empty() {
return (ImmutableArray2<K>) EMPTY;
}
/**
* Get an iterator over all entries.
*
* @return the iterator
*/
@Override
public Iterator<K> iterator() {
return new Iterator<K>() {
ImmutableArray2<K> a = ImmutableArray2.this;
int index;
@Override
public boolean hasNext() {
return index < a.length();
}
@Override
public K next() {
return a.get(index++);
}
@Override
public void remove() {
throw DataUtils.newUnsupportedOperationException("remove");
}
};
}
}
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.dev.util;
import java.util.Iterator;
import org.h2.mvstore.DataUtils;
/**
* An immutable array.
*
* @param <K> the type
*/
public abstract class ImmutableArray3<K> implements Iterable<K> {
private static final int MAX_LEVEL = 4;
private static final ImmutableArray3<?> EMPTY = new Plain<Object>(new Object[0]);
/**
* Get the length.
*
* @return the length
*/
public abstract int length();
/**
* Get the entry at this index.
*
* @param index the index
* @return the entry
*/
public abstract K get(int index);
/**
* Set the entry at this index.
*
* @param index the index
* @param obj the object
* @return the new immutable array
*/
public abstract ImmutableArray3<K> set(int index, K obj);
/**
* Insert an entry at this index.
*
* @param index the index
* @param obj the object
* @return the new immutable array
*/
public abstract ImmutableArray3<K> insert(int index, K obj);
/**
* Remove the entry at this index.
*
* @param index the index
* @return the new immutable array
*/
public abstract ImmutableArray3<K> remove(int index);
/**
* Get a sub-array.
*
* @param fromIndex the index of the first entry
* @param toIndex the end index, plus one
* @return the new immutable array
*/
public ImmutableArray3<K> subArray(int fromIndex, int toIndex) {
int len = toIndex - fromIndex;
@SuppressWarnings("unchecked")
K[] array = (K[]) new Object[len];
for (int i = 0; i < len; i++) {
array[i] = get(fromIndex + i);
}
return new Plain<K>(array);
}
/**
* Create an immutable array.
*
* @param array the data
* @return the new immutable array
*/
public static <K> ImmutableArray3<K> create(K... array) {
return new Plain<K>(array);
}
/**
* Get the data.
*
* @return the data
*/
public K[] array() {
int len = length();
@SuppressWarnings("unchecked")
K[] array = (K[]) new Object[len];
for (int i = 0; i < len; i++) {
array[i] = get(i);
}
return array;
}
abstract int level();
@Override
public String toString() {
StringBuilder buff = new StringBuilder();
for (K obj : this) {
buff.append(' ').append(obj);
}
return buff.toString();
}
/**
* Get an empty immutable array.
*
* @param <K> the key type
* @return the array
*/
@SuppressWarnings("unchecked")
public static <K> ImmutableArray3<K> empty() {
return (ImmutableArray3<K>) EMPTY;
}
/**
* Get an iterator over all entries.
*
* @return the iterator
*/
@Override
public Iterator<K> iterator() {
return new Iterator<K>() {
ImmutableArray3<K> a = ImmutableArray3.this;
int index;
@Override
public boolean hasNext() {
return index < a.length();
}
@Override
public K next() {
return a.get(index++);
}
@Override
public void remove() {
throw DataUtils.newUnsupportedOperationException("remove");
}
};
}
/**
* An immutable array backed by an array.
*
* @param <K> the type
*/
static class Plain<K> extends ImmutableArray3<K> {
/**
* The array.
*/
final K[] array;
public Plain(K[] array) {
this.array = array;
}
@Override
public K get(int index) {
return array[index];
}
@Override
public int length() {
return array.length;
}
@Override
public ImmutableArray3<K> set(int index, K obj) {
return new Set<K>(this, index, obj);
}
@Override
public ImmutableArray3<K> insert(int index, K obj) {
return new Insert<K>(this, index, obj);
}
@Override
public ImmutableArray3<K> remove(int index) {
return new Remove<K>(this, index);
}
static <K> ImmutableArray3<K> set(ImmutableArray3<K> base, int index, K obj) {
int len = base.length();
@SuppressWarnings("unchecked")
K[] array = (K[]) new Object[len];
for (int i = 0; i < len; i++) {
array[i] = i == index ? obj : base.get(i);
}
return new Plain<K>(array);
}
static <K> ImmutableArray3<K> insert(ImmutableArray3<K> base, int index, K obj) {
int len = base.length() + 1;
@SuppressWarnings("unchecked")
K[] array = (K[]) new Object[len];
for (int i = 0; i < len; i++) {
array[i] = i == index ? obj : i < index ? base.get(i) : base.get(i - 1);
}
return new Plain<K>(array);
}
static <K> ImmutableArray3<K> remove(ImmutableArray3<K> base, int index) {
int len = base.length() - 1;
@SuppressWarnings("unchecked")
K[] array = (K[]) new Object[len];
for (int i = 0; i < len; i++) {
array[i] = i < index ? base.get(i) : base.get(i + 1);
}
return new Plain<K>(array);
}
@Override
int level() {
return 0;
}
}
/**
* An immutable array backed by another immutable array, with one element
* changed.
*
* @param <K> the type
*/
static class Set<K> extends ImmutableArray3<K> {
final int index;
final ImmutableArray3<K> base;
final K obj;
Set(ImmutableArray3<K> base, int index, K obj) {
this.base = base;
this.index = index;
this.obj = obj;
}
@Override
public int length() {
return base.length();
}
@Override
public K get(int index) {
return this.index == index ? obj : base.get(index);
}
@Override
public ImmutableArray3<K> set(int index, K obj) {
if (index == this.index) {
return new Set<K>(base, index, obj);
} else if (level() < MAX_LEVEL) {
return new Set<K>(this, index, obj);
}
return Plain.set(this, index, obj);
}
@Override
public ImmutableArray3<K> insert(int index, K obj) {
if (level() < MAX_LEVEL) {
return new Insert<K>(this, index, obj);
}
return Plain.insert(this, index, obj);
}
@Override
public ImmutableArray3<K> remove(int index) {
if (level() < MAX_LEVEL) {
return new Remove<K>(this, index);
}
return Plain.remove(this, index);
}
@Override
int level() {
return base.level() + 1;
}
}
/**
* An immutable array backed by another immutable array, with one element
* added.
*
* @param <K> the type
*/
static class Insert<K> extends ImmutableArray3<K> {
final int index;
final ImmutableArray3<K> base;
final K obj;
Insert(ImmutableArray3<K> base, int index, K obj) {
this.base = base;
this.index = index;
this.obj = obj;
}
@Override
public ImmutableArray3<K> set(int index, K obj) {
if (level() < MAX_LEVEL) {
return new Set<K>(this, index, obj);
}
return Plain.set(this, index, obj);
}
@Override
public ImmutableArray3<K> insert(int index, K obj) {
if (level() < MAX_LEVEL) {
return new Insert<K>(this, index, obj);
}
return Plain.insert(this, index, obj);
}
@Override
public ImmutableArray3<K> remove(int index) {
if (index == this.index) {
return base;
} else if (level() < MAX_LEVEL) {
return new Remove<K>(this, index);
}
return Plain.remove(this, index);
}
@Override
public int length() {
return base.length() + 1;
}
@Override
public K get(int index) {
if (index == this.index) {
return obj;
} else if (index < this.index) {
return base.get(index);
}
return base.get(index - 1);
}
@Override
int level() {
return base.level() + 1;
}
}
/**
* An immutable array backed by another immutable array, with one element
* removed.
*
* @param <K> the type
*/
static class Remove<K> extends ImmutableArray3<K> {
final int index;
final ImmutableArray3<K> base;
Remove(ImmutableArray3<K> base, int index) {
this.base = base;
this.index = index;
}
@Override
public ImmutableArray3<K> set(int index, K obj) {
if (level() < MAX_LEVEL) {
return new Set<K>(this, index, obj);
}
return Plain.set(this, index, obj);
}
@Override
public ImmutableArray3<K> insert(int index, K obj) {
if (index == this.index) {
return base.set(index, obj);
} else if (level() < MAX_LEVEL) {
return new Insert<K>(this, index, obj);
}
return Plain.insert(this, index, obj);
}
@Override
public ImmutableArray3<K> remove(int index) {
if (level() < MAX_LEVEL) {
return new Remove<K>(this, index);
}
return Plain.remove(this, index);
}
@Override
public int length() {
return base.length() - 1;
}
@Override
public K get(int index) {
if (index < this.index) {
return base.get(index);
}
return base.get(index + 1);
}
@Override
int level() {
return base.level() + 1;
}
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论