提交 6547ef35 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Remove unused ValueHashMap

上级 52b412bd
/*
* Copyright 2004-2019 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.util;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import org.h2.message.DbException;
import org.h2.value.Value;
import org.h2.value.ValueNull;
/**
* This hash map supports keys of type Value.
* <p>
* ValueHashMap is a very simple implementation without allocation of additional
* objects for entries. It's very fast with good distribution of hashes, but if
* hashes have a lot of collisions this implementation tends to be very slow.
* <p>
* HashMap in archaic versions of Java have some overhead for allocation of
* entries, but slightly better behaviour with limited number of collisions,
* because collisions have no impact on non-colliding entries. HashMap in modern
* versions of Java also have the same overhead, but it builds a trees of keys
* with colliding hashes, that's why even if the all keys have exactly the same
* hash code it still offers a good performance similar to TreeMap. So
* ValueHashMap is faster in typical cases, but may behave really bad in some
* cases. HashMap is slower in typical cases, but its performance does not
* degrade too much even in the worst possible case (if keys are comparable).
*
* @param <V> the value type
*/
public class ValueHashMap<V> extends HashBase {
/**
* Keys array.
*/
Value[] keys;
/**
* Values array.
*/
V[] values;
@Override
@SuppressWarnings("unchecked")
protected void reset(int newLevel) {
super.reset(newLevel);
keys = new Value[len];
values = (V[]) new Object[len];
}
@Override
protected void rehash(int newLevel) {
Value[] oldKeys = keys;
V[] oldValues = values;
reset(newLevel);
int len = oldKeys.length;
for (int i = 0; i < len; i++) {
Value k = oldKeys[i];
if (k != null && k != ValueNull.DELETED) {
// skip the checkSizePut so we don't end up
// accidentally recursing
internalPut(k, oldValues[i], false);
}
}
}
private int getIndex(Value key) {
int h = key.hashCode();
/*
* Add some protection against hashes with the same less significant bits
* (ValueDouble with integer values, for example).
*/
return (h ^ h >>> 16) & mask;
}
/**
* Add or update a key value pair.
*
* @param key the key
* @param value the new value
*/
public void put(Value key, V value) {
checkSizePut();
internalPut(key, value, false);
}
/**
* Add a key value pair, values for existing keys are not replaced.
*
* @param key the key
* @param value the new value
*/
public void putIfAbsent(Value key, V value) {
checkSizePut();
internalPut(key, value, true);
}
private void internalPut(Value key, V value, boolean ifAbsent) {
int index = getIndex(key);
int plus = 1;
int deleted = -1;
do {
Value k = keys[index];
if (k == null) {
// found an empty record
if (deleted >= 0) {
index = deleted;
deletedCount--;
}
size++;
keys[index] = key;
values[index] = value;
return;
} else if (k == ValueNull.DELETED) {
// found a deleted record
if (deleted < 0) {
deleted = index;
}
} else if (k.equals(key)) {
if (ifAbsent) {
return;
}
// update existing
values[index] = value;
return;
}
index = (index + plus++) & mask;
} while (plus <= len);
// no space
DbException.throwInternalError("hashmap is full");
}
/**
* Remove a key value pair.
*
* @param key the key
*/
public void remove(Value key) {
checkSizeRemove();
int index = getIndex(key);
int plus = 1;
do {
Value k = keys[index];
if (k == null) {
// found an empty record
return;
} else if (k == ValueNull.DELETED) {
// found a deleted record
} else if (k.equals(key)) {
// found the record
keys[index] = ValueNull.DELETED;
values[index] = null;
deletedCount++;
size--;
return;
}
index = (index + plus++) & mask;
} while (plus <= len);
// not found
}
/**
* Get the value for this key. This method returns null if the key was not
* found.
*
* @param key the key
* @return the value for the given key
*/
public V get(Value key) {
int index = getIndex(key);
int plus = 1;
do {
Value k = keys[index];
if (k == null) {
// found an empty record
return null;
} else if (k == ValueNull.DELETED) {
// found a deleted record
} else if (k.equals(key)) {
// found it
return values[index];
}
index = (index + plus++) & mask;
} while (plus <= len);
return null;
}
/**
* Get the keys.
*
* @return all keys
*/
public Iterable<Value> keys() {
return new KeyIterable();
}
private final class KeyIterable implements Iterable<Value> {
KeyIterable() {
}
@Override
public Iterator<Value> iterator() {
return new UnifiedIterator<>(false);
}
}
/**
* Gets all map's entries.
*
* @return all map's entries.
*/
public Iterable<Map.Entry<Value, V>> entries() {
return new EntryIterable();
}
private final class EntryIterable implements Iterable<Map.Entry<Value, V>> {
EntryIterable() {
}
@Override
public Iterator<Map.Entry<Value, V>> iterator() {
return new UnifiedIterator<>(true);
}
}
final class UnifiedIterator<T> implements Iterator<T> {
int keysIndex = -1;
int left = size;
private final boolean forEntries;
UnifiedIterator(boolean forEntries) {
this.forEntries = forEntries;
}
@Override
public boolean hasNext() {
return left > 0;
}
@SuppressWarnings("unchecked")
@Override
public T next() {
if (left <= 0)
throw new NoSuchElementException();
left--;
for (;;) {
keysIndex++;
Value key = keys[keysIndex];
if (key != null && key != ValueNull.DELETED) {
return (T) (forEntries ? new AbstractMap.SimpleImmutableEntry<>(key, values[keysIndex]) : key);
}
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
/**
* Get the list of values.
*
* @return all values
*/
public ArrayList<V> values() {
ArrayList<V> list = new ArrayList<>(size);
int len = keys.length;
for (int i = 0; i < len; i++) {
Value k = keys[i];
if (k != null && k != ValueNull.DELETED) {
list.add(values[i]);
}
}
return list;
}
}
......@@ -229,7 +229,6 @@ import org.h2.test.unit.TestTools;
import org.h2.test.unit.TestTraceSystem;
import org.h2.test.unit.TestUtils;
import org.h2.test.unit.TestValue;
import org.h2.test.unit.TestValueHashMap;
import org.h2.test.unit.TestValueMemory;
import org.h2.test.utils.OutputCatcher;
import org.h2.test.utils.SelfDestructor;
......@@ -994,7 +993,6 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
addTest(new TestStringUtils());
addTest(new TestTraceSystem());
addTest(new TestUtils());
addTest(new TestValueHashMap());
addTest(new TestLocalResultFactory());
runAddedTests();
......
/*
* Copyright 2004-2019 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.unit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Random;
import org.h2.api.JavaObjectSerializer;
import org.h2.store.DataHandler;
import org.h2.store.FileStore;
import org.h2.store.LobStorageBackend;
import org.h2.test.TestBase;
import org.h2.util.SmallLRUCache;
import org.h2.util.TempFileDeleter;
import org.h2.util.ValueHashMap;
import org.h2.value.CompareMode;
import org.h2.value.Value;
import org.h2.value.ValueDouble;
import org.h2.value.ValueInt;
/**
* Tests the value hash map.
*/
public class TestValueHashMap extends TestBase implements DataHandler {
CompareMode compareMode = CompareMode.getInstance(null, 0);
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
@Override
public void test() {
testNotANumber();
testRandomized();
}
private void testNotANumber() {
ValueHashMap<Integer> map = new ValueHashMap<>();
for (int i = 1; i < 100; i++) {
double d = Double.longBitsToDouble(0x7ff0000000000000L | i);
ValueDouble v = ValueDouble.get(d);
map.put(v, null);
assertEquals(1, map.size());
}
}
private void testRandomized() {
ValueHashMap<Value> map = new ValueHashMap<>();
HashMap<Value, Value> hash = new HashMap<>();
Random random = new Random(1);
Comparator<Value> vc = new Comparator<Value>() {
@Override
public int compare(Value v1, Value v2) {
return v1.compareTo(v2, null, compareMode);
}
};
for (int i = 0; i < 10000; i++) {
int op = random.nextInt(10);
Value key = ValueInt.get(random.nextInt(100));
Value value = ValueInt.get(random.nextInt(100));
switch (op) {
case 0:
map.put(key, value);
hash.put(key, value);
break;
case 1:
map.remove(key);
hash.remove(key);
break;
case 2:
Value v1 = map.get(key);
Value v2 = hash.get(key);
assertTrue(v1 == null ? v2 == null : v1.equals(v2));
break;
case 3: {
ArrayList<Value> a1 = new ArrayList<>();
for (Value v : map.keys()) {
a1.add(v);
}
ArrayList<Value> a2 = new ArrayList<>(hash.keySet());
assertEquals(a1.size(), a2.size());
Collections.sort(a1, vc);
Collections.sort(a2, vc);
for (int j = 0; j < a1.size(); j++) {
assertTrue(a1.get(j).equals(a2.get(j)));
}
break;
}
case 4:
ArrayList<Value> a1 = map.values();
ArrayList<Value> a2 = new ArrayList<>(hash.values());
assertEquals(a1.size(), a2.size());
Collections.sort(a1, vc);
Collections.sort(a2, vc);
for (int j = 0; j < a1.size(); j++) {
assertTrue(a1.get(j).equals(a2.get(j)));
}
break;
default:
}
}
}
@Override
public String getDatabasePath() {
return null;
}
@Override
public FileStore openFile(String name, String mode, boolean mustExist) {
return null;
}
@Override
public void checkPowerOff() {
// nothing to do
}
@Override
public void checkWritingAllowed() {
// nothing to do
}
@Override
public int getMaxLengthInplaceLob() {
return 0;
}
@Override
public String getLobCompressionAlgorithm(int type) {
return null;
}
@Override
public Object getLobSyncObject() {
return this;
}
@Override
public SmallLRUCache<String, String[]> getLobFileListCache() {
return null;
}
@Override
public TempFileDeleter getTempFileDeleter() {
return TempFileDeleter.getInstance();
}
@Override
public LobStorageBackend getLobStorage() {
return null;
}
@Override
public int readLob(long lobId, byte[] hmac, long offset, byte[] buff,
int off, int length) {
return -1;
}
@Override
public JavaObjectSerializer getJavaObjectSerializer() {
return null;
}
@Override
public CompareMode getCompareMode() {
return compareMode;
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论