mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-08-12 10:45:37 +02:00
Use prime numbers for hash table sizes to reduce collisions.
Change-Id: I4233e4a4ca729dd742825ee23b9c254fa836bc41
This commit is contained in:
parent
eeaed8ee74
commit
9622166291
5 changed files with 269 additions and 188 deletions
|
@ -14,6 +14,8 @@
|
||||||
*/
|
*/
|
||||||
package org.eclipse.cdt.core.parser.tests;
|
package org.eclipse.cdt.core.parser.tests;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
import org.eclipse.cdt.core.parser.util.CharArrayObjectMap;
|
import org.eclipse.cdt.core.parser.util.CharArrayObjectMap;
|
||||||
import org.eclipse.cdt.core.parser.util.ObjectMap;
|
import org.eclipse.cdt.core.parser.util.ObjectMap;
|
||||||
|
|
||||||
|
@ -24,7 +26,9 @@ import junit.framework.TestCase;
|
||||||
*/
|
*/
|
||||||
public class ObjectMapTest extends TestCase {
|
public class ObjectMapTest extends TestCase {
|
||||||
|
|
||||||
static public class HashObject {
|
private static class HashObject {
|
||||||
|
final public int hash;
|
||||||
|
|
||||||
HashObject(int h) {
|
HashObject(int h) {
|
||||||
hash = h;
|
hash = h;
|
||||||
}
|
}
|
||||||
|
@ -33,7 +37,6 @@ public class ObjectMapTest extends TestCase {
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
final public int hash;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void insertContents(ObjectMap map, Object[][] contents) throws Exception {
|
public void insertContents(ObjectMap map, Object[][] contents) throws Exception {
|
||||||
|
@ -60,7 +63,7 @@ public class ObjectMapTest extends TestCase {
|
||||||
assertContents(map, contents);
|
assertContents(map, contents);
|
||||||
|
|
||||||
assertEquals(map.size(), 1);
|
assertEquals(map.size(), 1);
|
||||||
assertEquals(map.capacity(), 2);
|
assertEquals(map.capacity(), 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSimpleCollision() throws Exception{
|
public void testSimpleCollision() throws Exception{
|
||||||
|
@ -75,7 +78,7 @@ public class ObjectMapTest extends TestCase {
|
||||||
insertContents(map, contents);
|
insertContents(map, contents);
|
||||||
|
|
||||||
assertEquals(map.size(), 2);
|
assertEquals(map.size(), 2);
|
||||||
assertEquals(map.capacity(), 2);
|
assertEquals(map.capacity(), 8);
|
||||||
|
|
||||||
assertContents(map, contents);
|
assertContents(map, contents);
|
||||||
}
|
}
|
||||||
|
@ -84,7 +87,7 @@ public class ObjectMapTest extends TestCase {
|
||||||
ObjectMap map = new ObjectMap(1);
|
ObjectMap map = new ObjectMap(1);
|
||||||
|
|
||||||
assertEquals(map.size(), 0);
|
assertEquals(map.size(), 0);
|
||||||
assertEquals(map.capacity(), 2);
|
assertEquals(map.capacity(), 8);
|
||||||
|
|
||||||
Object[][] res = new Object[][] { { "0", "o0" },
|
Object[][] res = new Object[][] { { "0", "o0" },
|
||||||
{ "1", "o1" },
|
{ "1", "o1" },
|
||||||
|
@ -101,7 +104,7 @@ public class ObjectMapTest extends TestCase {
|
||||||
ObjectMap map = new ObjectMap(1);
|
ObjectMap map = new ObjectMap(1);
|
||||||
|
|
||||||
assertEquals(map.size(), 0);
|
assertEquals(map.size(), 0);
|
||||||
assertEquals(map.capacity(), 2);
|
assertEquals(map.capacity(), 8);
|
||||||
|
|
||||||
Object[][] res = new Object[][] { { new HashObject(0), "o0" },
|
Object[][] res = new Object[][] { { new HashObject(0), "o0" },
|
||||||
{ new HashObject(1), "o1" },
|
{ new HashObject(1), "o1" },
|
||||||
|
@ -118,13 +121,13 @@ public class ObjectMapTest extends TestCase {
|
||||||
ObjectMap map = new ObjectMap(1);
|
ObjectMap map = new ObjectMap(1);
|
||||||
|
|
||||||
assertEquals(map.size(), 0);
|
assertEquals(map.size(), 0);
|
||||||
assertEquals(map.capacity(), 2);
|
assertEquals(map.capacity(), 8);
|
||||||
|
|
||||||
Object[][] res = new Object[][] { { "0", "o0" },
|
Object[][] res = new Object[][] { { "0", "o0" },
|
||||||
{ "1", "o1" } };
|
{ "1", "o1" } };
|
||||||
|
|
||||||
insertContents(map, res);
|
insertContents(map, res);
|
||||||
assertEquals(map.capacity(), 2);
|
assertEquals(map.capacity(), 8);
|
||||||
assertContents(map, res);
|
assertContents(map, res);
|
||||||
|
|
||||||
res = new Object[][]{ { "0", "o00" },
|
res = new Object[][]{ { "0", "o00" },
|
||||||
|
@ -136,21 +139,6 @@ public class ObjectMapTest extends TestCase {
|
||||||
assertContents(map, res);
|
assertContents(map, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testResizeResolvesCollision() throws Exception{
|
|
||||||
ObjectMap map = new ObjectMap(2);
|
|
||||||
|
|
||||||
Object k1 = new HashObject(0);
|
|
||||||
Object k2 = new HashObject(1);
|
|
||||||
Object k3 = new HashObject(4); // Collision with 0 in a table capacity 2, but ok in table capacity 4
|
|
||||||
|
|
||||||
Object[][] con = new Object[][] { { k1, "1" },
|
|
||||||
{ k2, "2" },
|
|
||||||
{ k3, "3" } };
|
|
||||||
|
|
||||||
insertContents(map, con);
|
|
||||||
assertContents(map, con);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testMapAdd() {
|
public void testMapAdd() {
|
||||||
CharArrayObjectMap map = new CharArrayObjectMap(4);
|
CharArrayObjectMap map = new CharArrayObjectMap(4);
|
||||||
char[] key1 = "key1".toCharArray();
|
char[] key1 = "key1".toCharArray();
|
||||||
|
@ -161,14 +149,25 @@ public class ObjectMapTest extends TestCase {
|
||||||
Object value2 = map.get(key2);
|
Object value2 = map.get(key2);
|
||||||
assertEquals(value1, value2);
|
assertEquals(value1, value2);
|
||||||
|
|
||||||
for (int i = 0; i < 5; ++i) {
|
for (int i = 0; i < 25; ++i) {
|
||||||
map.put(("ikey" + i).toCharArray(), new Integer(i));
|
map.put(("ikey" + i).toCharArray(), new Integer(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
Object ivalue1 = map.get("ikey1".toCharArray());
|
for (int i = 0; i < 25; ++i) {
|
||||||
assertEquals(ivalue1, new Integer(1));
|
Object ivalue1 = map.get(("ikey" + i).toCharArray());
|
||||||
|
assertEquals(i, ivalue1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Object ivalue4 = map.get("ikey4".toCharArray());
|
public void testCollisionRatio() {
|
||||||
assertEquals(ivalue4, new Integer(4));
|
Random random = new Random(239);
|
||||||
|
CharArrayObjectMap map = new CharArrayObjectMap(1);
|
||||||
|
for (int i = 0; i < 20000; i++) {
|
||||||
|
int r = random.nextInt();
|
||||||
|
map.put(("key" + Integer.toUnsignedString(i)).toCharArray(), i);
|
||||||
|
double collisionRatio = (double) map.countCollisions() / map.size();
|
||||||
|
assertTrue(String.format("Collision ratio %.3f is unexpectedly high for map size of %d.", collisionRatio, map.size()),
|
||||||
|
collisionRatio <= 0.4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ public class CharTable extends HashTable {
|
||||||
super(initialSize);
|
super(initialSize);
|
||||||
keyTable = new char[capacity()][];
|
keyTable = new char[capacity()][];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resize(int size) {
|
protected void resize(int size) {
|
||||||
char[][] oldKeyTable = keyTable;
|
char[][] oldKeyTable = keyTable;
|
||||||
|
@ -33,34 +33,34 @@ public class CharTable extends HashTable {
|
||||||
System.arraycopy(oldKeyTable, 0, keyTable, 0, Math.min(size, oldKeyTable.length));
|
System.arraycopy(oldKeyTable, 0, keyTable, 0, Math.min(size, oldKeyTable.length));
|
||||||
super.resize(size);
|
super.resize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
super.clear();
|
super.clear();
|
||||||
for (int i = 0; i < capacity(); i++)
|
for (int i = 0; i < capacity(); i++)
|
||||||
keyTable[i] = null;
|
keyTable[i] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object clone() {
|
public Object clone() {
|
||||||
CharTable newTable = (CharTable) super.clone();
|
CharTable newTable = (CharTable) super.clone();
|
||||||
|
|
||||||
int size = capacity();
|
int size = capacity();
|
||||||
newTable.keyTable = new char[size][];
|
newTable.keyTable = new char[size][];
|
||||||
System.arraycopy(keyTable, 0, newTable.keyTable, 0, keyTable.length);
|
System.arraycopy(keyTable, 0, newTable.keyTable, 0, keyTable.length);
|
||||||
|
|
||||||
return newTable;
|
return newTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final int hash(char[] source, int start, int length) {
|
protected final int hash(char[] source, int start, int length) {
|
||||||
return CharArrayUtils.hash(source, start, length) & ((keyTable.length * 2) - 1);
|
return hashTable == null ? 0 : hashToOffset(CharArrayUtils.hash(source, start, length));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final int hash(int pos) {
|
protected final int hash(int pos) {
|
||||||
return hash(keyTable[pos], 0, keyTable[pos].length);
|
return hash(keyTable[pos], 0, keyTable[pos].length);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final int hash(char[] obj) {
|
protected final int hash(char[] obj) {
|
||||||
return hash(obj, 0, obj.length);
|
return hash(obj, 0, obj.length);
|
||||||
}
|
}
|
||||||
|
@ -68,52 +68,52 @@ public class CharTable extends HashTable {
|
||||||
protected final int addIndex(char[] buffer) {
|
protected final int addIndex(char[] buffer) {
|
||||||
return addIndex(buffer, 0, buffer.length);
|
return addIndex(buffer, 0, buffer.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int addIndex(char[] buffer, int start, int len) {
|
public final int addIndex(char[] buffer, int start, int len) {
|
||||||
if (hashTable != null) {
|
if (hashTable == null) {
|
||||||
int hash = hash(buffer, start, len);
|
|
||||||
int pos = lookup(buffer, start, len, hash);
|
|
||||||
if (pos != -1)
|
|
||||||
return pos;
|
|
||||||
|
|
||||||
// key is not here, add it.
|
|
||||||
if ((currEntry + 1) >= capacity()) {
|
|
||||||
resize();
|
|
||||||
hash = hash(buffer, start, len);
|
|
||||||
}
|
|
||||||
currEntry++;
|
|
||||||
keyTable[currEntry] = CharArrayUtils.extract(buffer, start, len);
|
|
||||||
linkIntoHashTable(currEntry, hash);
|
|
||||||
} else {
|
|
||||||
int pos = lookup(buffer, start, len);
|
int pos = lookup(buffer, start, len);
|
||||||
if (pos != -1)
|
if (pos != -1)
|
||||||
return pos;
|
return pos;
|
||||||
// key is not here, add it.
|
// Key is not here, add it.
|
||||||
if ((currEntry + 1) >= capacity()) {
|
if (currEntry + 1 >= capacity()) {
|
||||||
resize();
|
resize();
|
||||||
if (capacity() > minHashSize) {
|
if (hashTable != null) {
|
||||||
//if we grew from list to hash, then recurse and add as a hashtable
|
// If we grew from list to hash, then recurse and add as a hashtable.
|
||||||
return addIndex(buffer, start, len);
|
return addIndex(buffer, start, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currEntry++;
|
currEntry++;
|
||||||
keyTable[currEntry] = CharArrayUtils.extract(buffer, start, len);
|
keyTable[currEntry] = CharArrayUtils.extract(buffer, start, len);
|
||||||
|
} else {
|
||||||
|
int hash = hash(buffer, start, len);
|
||||||
|
int pos = lookup(buffer, start, len, hash);
|
||||||
|
if (pos != -1)
|
||||||
|
return pos;
|
||||||
|
|
||||||
|
// Key is not here, add it.
|
||||||
|
if (currEntry + 1 >= capacity()) {
|
||||||
|
resize();
|
||||||
|
hash = hash(buffer, start, len);
|
||||||
|
}
|
||||||
|
currEntry++;
|
||||||
|
keyTable[currEntry] = CharArrayUtils.extract(buffer, start, len);
|
||||||
|
linkIntoHashTable(currEntry, hash);
|
||||||
}
|
}
|
||||||
return currEntry;
|
return currEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void removeEntry(int i) {
|
protected void removeEntry(int i) {
|
||||||
// Remove the entry from the keyTable, shifting everything over if necessary
|
// Remove the entry from the keyTable, shifting everything over if necessary.
|
||||||
int hash = hash(keyTable[i]);
|
int hash = hash(keyTable[i]);
|
||||||
if (i < currEntry)
|
if (i < currEntry)
|
||||||
System.arraycopy(keyTable, i + 1, keyTable, i, currEntry - i);
|
System.arraycopy(keyTable, i + 1, keyTable, i, currEntry - i);
|
||||||
|
|
||||||
keyTable[currEntry] = null;
|
keyTable[currEntry] = null;
|
||||||
|
|
||||||
// Make sure you remove the value before calling super where currEntry will change
|
// Make sure you remove the value before calling super where currEntry will change
|
||||||
removeEntry(i, hash);
|
removeEntry(i, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<char[]> toList() {
|
public List<char[]> toList() {
|
||||||
List<char[]> list = new ArrayList<char[]>(size());
|
List<char[]> list = new ArrayList<char[]>(size());
|
||||||
int size = size();
|
int size = size();
|
||||||
|
@ -122,34 +122,34 @@ public class CharTable extends HashTable {
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final char[] keyAt(int i) {
|
public final char[] keyAt(int i) {
|
||||||
if (i < 0 || i > currEntry)
|
if (i < 0 || i > currEntry)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return keyTable[i];
|
return keyTable[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean containsKey(char[] key, int start, int len) {
|
public final boolean containsKey(char[] key, int start, int len) {
|
||||||
return lookup(key, start, len) != -1;
|
return lookup(key, start, len) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean containsKey(char[] key) {
|
public final boolean containsKey(char[] key) {
|
||||||
return lookup(key) != -1;
|
return lookup(key) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final char[] findKey(char[] buffer, int start, int len) {
|
public final char[] findKey(char[] buffer, int start, int len) {
|
||||||
int idx = lookup(buffer, start, len);
|
int idx = lookup(buffer, start, len);
|
||||||
if (idx == -1)
|
if (idx == -1)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return keyTable[idx];
|
return keyTable[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
public int lookup(char[] buffer) {
|
public int lookup(char[] buffer) {
|
||||||
return lookup(buffer, 0, buffer.length);
|
return lookup(buffer, 0, buffer.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final int lookup(char[] buffer, int start, int len) {
|
protected final int lookup(char[] buffer, int start, int len) {
|
||||||
if (hashTable != null)
|
if (hashTable != null)
|
||||||
return lookup(buffer, start, len, hash(buffer, start, len));
|
return lookup(buffer, start, len, hash(buffer, start, len));
|
||||||
|
@ -159,24 +159,25 @@ public class CharTable extends HashTable {
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final int lookup(char[] buffer, int start, int len, int hash) {
|
protected final int lookup(char[] buffer, int start, int len, int hash) {
|
||||||
if (hashTable[hash] == 0)
|
int i = hashTable[hash];
|
||||||
|
if (i == 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
int i = hashTable[hash] - 1;
|
--i;
|
||||||
if (CharArrayUtils.equals(buffer, start, len, keyTable[i]))
|
if (CharArrayUtils.equals(buffer, start, len, keyTable[i]))
|
||||||
return i;
|
return i;
|
||||||
|
|
||||||
// Follow the next chain
|
// Follow the next chain
|
||||||
for (i = nextTable[i] - 1; i >= 0 && nextTable[i] != i + 1; i = nextTable[i] - 1) {
|
for (i = nextTable[i] - 1; i >= 0 && i != nextTable[i] - 1; i = nextTable[i] - 1) {
|
||||||
if (CharArrayUtils.equals(buffer, start, len, keyTable[i]))
|
if (CharArrayUtils.equals(buffer, start, len, keyTable[i]))
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 5.7
|
* @since 5.7
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -10,40 +10,72 @@
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.core.parser.util;
|
package org.eclipse.cdt.core.parser.util;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ddaoust
|
* @author ddaoust
|
||||||
*/
|
*/
|
||||||
public class HashTable implements Cloneable {
|
public class HashTable implements Cloneable {
|
||||||
protected static final int minHashSize = 2;
|
// Prime numbers from http://planetmath.org/goodhashtableprimes
|
||||||
|
private static final int[] PRIMES = {
|
||||||
|
17,
|
||||||
|
29,
|
||||||
|
53,
|
||||||
|
97,
|
||||||
|
193,
|
||||||
|
389,
|
||||||
|
769,
|
||||||
|
1543,
|
||||||
|
3079,
|
||||||
|
6151,
|
||||||
|
12289,
|
||||||
|
24593,
|
||||||
|
49157,
|
||||||
|
98317,
|
||||||
|
196613,
|
||||||
|
393241,
|
||||||
|
786433,
|
||||||
|
1572869,
|
||||||
|
3145739,
|
||||||
|
6291469,
|
||||||
|
12582917,
|
||||||
|
25165843,
|
||||||
|
50331653,
|
||||||
|
100663319,
|
||||||
|
201326611,
|
||||||
|
402653189,
|
||||||
|
805306457,
|
||||||
|
1610612741
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final int MIN_HASH_SIZE = 9;
|
||||||
|
/** @deprecated Don't depend on this implementation detail. @noreference This field is not intended to be referenced by clients. */
|
||||||
|
@Deprecated
|
||||||
|
protected static final int minHashSize = MIN_HASH_SIZE - 1;
|
||||||
|
|
||||||
protected int currEntry = -1;
|
protected int currEntry = -1;
|
||||||
protected int[] hashTable;
|
protected int[] hashTable;
|
||||||
protected int[] nextTable;
|
protected int[] nextTable;
|
||||||
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return currEntry < 0;
|
return currEntry < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int size() {
|
public final int size() {
|
||||||
return currEntry + 1;
|
return currEntry + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashTable(int initialSize) {
|
public HashTable(int initialSize) {
|
||||||
int size = 1;
|
if (initialSize >= MIN_HASH_SIZE) {
|
||||||
while (size < initialSize)
|
hashTable = new int[getSuitableHashTableSize(initialSize)];
|
||||||
size <<= 1;
|
nextTable = new int[initialSize];
|
||||||
|
|
||||||
if (size > minHashSize) {
|
|
||||||
hashTable = new int[size * 2];
|
|
||||||
nextTable = new int[size];
|
|
||||||
} else {
|
} else {
|
||||||
hashTable = null;
|
hashTable = null;
|
||||||
nextTable = null;
|
nextTable = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object clone() {
|
public Object clone() {
|
||||||
HashTable newTable = null;
|
HashTable newTable = null;
|
||||||
|
@ -53,11 +85,11 @@ public class HashTable implements Cloneable {
|
||||||
// Shouldn't happen because object supports clone.
|
// Shouldn't happen because object supports clone.
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int size = capacity();
|
int size = capacity();
|
||||||
|
|
||||||
if (hashTable != null) {
|
if (hashTable != null) {
|
||||||
newTable.hashTable = new int[size * 2];
|
newTable.hashTable = new int[getSuitableHashTableSize(size)];
|
||||||
newTable.nextTable = new int[size];
|
newTable.nextTable = new int[size];
|
||||||
System.arraycopy(hashTable, 0, newTable.hashTable, 0, hashTable.length);
|
System.arraycopy(hashTable, 0, newTable.hashTable, 0, hashTable.length);
|
||||||
System.arraycopy(nextTable, 0, newTable.nextTable, 0, nextTable.length);
|
System.arraycopy(nextTable, 0, newTable.nextTable, 0, nextTable.length);
|
||||||
|
@ -65,35 +97,29 @@ public class HashTable implements Cloneable {
|
||||||
newTable.currEntry = currEntry;
|
newTable.currEntry = currEntry;
|
||||||
return newTable;
|
return newTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void resize() {
|
protected void resize() {
|
||||||
resize(capacity() << 1);
|
resize(capacity() << 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
currEntry = -1;
|
currEntry = -1;
|
||||||
|
|
||||||
// Clear the table.
|
// Clear the table.
|
||||||
if (hashTable == null)
|
if (hashTable == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (int i = 0; i < capacity(); i++) {
|
Arrays.fill(hashTable, 0);
|
||||||
hashTable[2 * i] = 0;
|
Arrays.fill(nextTable, 0);
|
||||||
hashTable[2 * i + 1] = 0;
|
|
||||||
nextTable[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void rehash() {
|
protected void rehash() {
|
||||||
if (nextTable == null)
|
if (nextTable == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Clear the table (don't call clear() or else the subclasses stuff will be cleared too).
|
// Clear the table (don't call clear() or else the subclasses stuff will be cleared too).
|
||||||
for (int i = 0; i < capacity(); i++) {
|
Arrays.fill(hashTable, 0);
|
||||||
hashTable[2 * i] = 0;
|
Arrays.fill(nextTable, 0);
|
||||||
hashTable[2 * i + 1] = 0;
|
|
||||||
nextTable[i] = 0;
|
|
||||||
}
|
|
||||||
// Need to rehash everything.
|
// Need to rehash everything.
|
||||||
for (int i = 0; i <= currEntry; ++i) {
|
for (int i = 0; i <= currEntry; ++i) {
|
||||||
linkIntoHashTable(i, hash(i));
|
linkIntoHashTable(i, hash(i));
|
||||||
|
@ -101,77 +127,109 @@ public class HashTable implements Cloneable {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void resize(int size) {
|
protected void resize(int size) {
|
||||||
if (size > minHashSize) {
|
if (size >= MIN_HASH_SIZE) {
|
||||||
hashTable = new int[size * 2];
|
hashTable = new int[getSuitableHashTableSize(size)];
|
||||||
nextTable = new int[size];
|
nextTable = new int[size];
|
||||||
|
|
||||||
// Need to rehash everything.
|
// Need to rehash everything.
|
||||||
for (int i = 0; i <= currEntry; ++i) {
|
for (int i = 0; i <= currEntry; ++i) {
|
||||||
linkIntoHashTable(i, hash(i));
|
linkIntoHashTable(i, hash(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int getSuitableHashTableSize(int size) {
|
||||||
|
size += (size + 2) / 3;
|
||||||
|
if (size < 0)
|
||||||
|
return Integer.MAX_VALUE; // Integer overflow. Return the max possible size.
|
||||||
|
int low = 0;
|
||||||
|
int high = PRIMES.length;
|
||||||
|
while (low < high) {
|
||||||
|
int mid = (low + high) >>> 1;
|
||||||
|
int p = PRIMES[mid];
|
||||||
|
if (p < size) {
|
||||||
|
low = mid + 1;
|
||||||
|
} else if (p > size) {
|
||||||
|
high = mid;
|
||||||
|
} else {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (low == PRIMES.length)
|
||||||
|
return Integer.MAX_VALUE; // Largest prime is not sufficient. Return the max possible size.
|
||||||
|
|
||||||
|
return PRIMES[low];
|
||||||
|
}
|
||||||
|
|
||||||
protected int hash(int pos) {
|
protected int hash(int pos) {
|
||||||
// Return the hash value of the element in the key table.
|
// Return the hash value of the element in the key table.
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final int hashToOffset(int hash) {
|
||||||
|
int offset = hash % hashTable.length;
|
||||||
|
if (offset < 0)
|
||||||
|
offset += hashTable.length - 1;
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
protected void linkIntoHashTable(int i, int hash) {
|
protected void linkIntoHashTable(int i, int hash) {
|
||||||
if (nextTable == null)
|
if (nextTable == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (hashTable[hash] == 0) {
|
if (hashTable[hash] == 0) {
|
||||||
hashTable[hash] = i + 1;
|
hashTable[hash] = i + 1;
|
||||||
} else {
|
} else {
|
||||||
// Need to link.
|
// Need to link.
|
||||||
int j = hashTable[hash] - 1;
|
int j = hashTable[hash] - 1;
|
||||||
while (nextTable[j] != 0) {
|
int k;
|
||||||
// if (nextTable[j] - 1 == j) {
|
while ((k = nextTable[j]) != 0) {
|
||||||
// break;
|
j = k - 1;
|
||||||
// }
|
|
||||||
j = nextTable[j] - 1;
|
|
||||||
}
|
}
|
||||||
nextTable[j] = i + 1;
|
nextTable[j] = i + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int capacity() {
|
public final int capacity() {
|
||||||
if (nextTable == null)
|
if (nextTable == null)
|
||||||
return minHashSize;
|
return MIN_HASH_SIZE - 1;
|
||||||
return nextTable.length;
|
return nextTable.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void removeEntry(int i, int hash) {
|
protected void removeEntry(int i, int hash) {
|
||||||
if (nextTable == null) {
|
if (nextTable == null) {
|
||||||
--currEntry;
|
--currEntry;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the hash entry.
|
// Remove the hash entry.
|
||||||
if (hashTable[hash] == i + 1) {
|
if (hashTable[hash] == i + 1) {
|
||||||
hashTable[hash] = nextTable[i];
|
hashTable[hash] = nextTable[i];
|
||||||
} else {
|
} else {
|
||||||
// Find entry pointing to me.
|
// Find entry pointing to me.
|
||||||
int j = hashTable[hash] - 1;
|
int j = hashTable[hash] - 1;
|
||||||
while (nextTable[j] != 0 && nextTable[j] != i + 1)
|
int k;
|
||||||
j = nextTable[j] - 1;
|
while ((k = nextTable[j]) != 0 && k != i + 1) {
|
||||||
|
j = k - 1;
|
||||||
|
}
|
||||||
nextTable[j] = nextTable[i];
|
nextTable[j] = nextTable[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < currEntry) {
|
if (i < currEntry) {
|
||||||
// Shift everything over.
|
// Shift everything over.
|
||||||
System.arraycopy(nextTable, i + 1, nextTable, i, currEntry - i);
|
System.arraycopy(nextTable, i + 1, nextTable, i, currEntry - i);
|
||||||
|
|
||||||
// Adjust hash and next entries for things that moved.
|
// Adjust hash and next entries for things that moved.
|
||||||
for (int j = 0; j < hashTable.length; ++j) {
|
for (int j = 0; j < hashTable.length; ++j) {
|
||||||
if (hashTable[j] > i + 1)
|
int k = hashTable[j] - 1;
|
||||||
--hashTable[j];
|
if (k > i)
|
||||||
|
hashTable[j] = k;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j = 0; j < nextTable.length; ++j) {
|
for (int j = 0; j < nextTable.length; ++j) {
|
||||||
if (nextTable[j] > i + 1)
|
int k = nextTable[j] - 1;
|
||||||
--nextTable[j];
|
if (k > i)
|
||||||
|
nextTable[j] = k;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,37 +240,60 @@ public class HashTable implements Cloneable {
|
||||||
|
|
||||||
public final void sort(Comparator<Object> c) {
|
public final void sort(Comparator<Object> c) {
|
||||||
if (size() > 1) {
|
if (size() > 1) {
|
||||||
quickSort(c, 0, size() - 1);
|
quickSort(c, 0, size() - 1);
|
||||||
rehash();
|
rehash();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void quickSort(Comparator<Object> c, int p, int r) {
|
private void quickSort(Comparator<Object> c, int p, int r) {
|
||||||
if (p < r) {
|
if (p < r) {
|
||||||
int q = partition(c, p, r);
|
int q = partition(c, p, r);
|
||||||
if (p < q) quickSort(c, p, q);
|
if (p < q)
|
||||||
if (++q < r) quickSort(c, q, r);
|
quickSort(c, p, q);
|
||||||
|
if (++q < r)
|
||||||
|
quickSort(c, q, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int partition(Comparator<Object> c, int p, int r) {
|
protected int partition(Comparator<Object> c, int p, int r) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For debugging only.
|
||||||
|
* @noreference This method is not intended to be referenced by clients.
|
||||||
|
*/
|
||||||
public void dumpNexts() {
|
public void dumpNexts() {
|
||||||
if (nextTable == null)
|
if (nextTable == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (int i = 0; i < nextTable.length; ++i) {
|
for (int i = 0; i < nextTable.length; ++i) {
|
||||||
if (nextTable[i] == 0)
|
if (nextTable[i] == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
System.out.print(i);
|
System.out.print(i);
|
||||||
|
|
||||||
for (int j = nextTable[i] - 1; j >= 0; j = nextTable[j] - 1)
|
for (int j = nextTable[i] - 1; j >= 0; j = nextTable[j] - 1)
|
||||||
System.out.print(" -> " + j); //$NON-NLS-1$
|
System.out.print(" -> " + j); //$NON-NLS-1$
|
||||||
|
|
||||||
System.out.println(""); //$NON-NLS-1$
|
System.out.println(""); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of collisions.
|
||||||
|
* For debugging only.
|
||||||
|
* @noreference This method is not intended to be referenced by clients.
|
||||||
|
*/
|
||||||
|
public int countCollisions() {
|
||||||
|
if (nextTable == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int numCollisions = 0;
|
||||||
|
for (int i = 0; i < nextTable.length; ++i) {
|
||||||
|
if (nextTable[i] != 0)
|
||||||
|
numCollisions++;
|
||||||
|
}
|
||||||
|
return numCollisions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Andrew Niefer (IBM Corporation) - Initial API and implementation
|
* Andrew Niefer (IBM Corporation) - Initial API and implementation
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.core.parser.util;
|
package org.eclipse.cdt.core.parser.util;
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ public class ObjectMap extends ObjectTable<Object> {
|
||||||
@Override
|
@Override
|
||||||
public Object put(Object key, Object value) { throw new UnsupportedOperationException(); }
|
public Object put(Object key, Object value) { throw new UnsupportedOperationException(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
private Object[] valueTable;
|
private Object[] valueTable;
|
||||||
|
|
||||||
public ObjectMap(int initialSize) {
|
public ObjectMap(int initialSize) {
|
||||||
|
@ -39,13 +39,13 @@ public class ObjectMap extends ObjectTable<Object> {
|
||||||
System.arraycopy(valueTable, 0, newMap.valueTable, 0, valueTable.length);
|
System.arraycopy(valueTable, 0, newMap.valueTable, 0, valueTable.length);
|
||||||
return newMap;
|
return newMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
final public void clear() {
|
final public void clear() {
|
||||||
super.clear();
|
super.clear();
|
||||||
Arrays.fill(valueTable, null);
|
Arrays.fill(valueTable, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resize(int size) {
|
protected void resize(int size) {
|
||||||
Object[] oldValueTable = valueTable;
|
Object[] oldValueTable = valueTable;
|
||||||
|
@ -53,7 +53,7 @@ public class ObjectMap extends ObjectTable<Object> {
|
||||||
System.arraycopy(oldValueTable, 0, valueTable, 0, oldValueTable.length);
|
System.arraycopy(oldValueTable, 0, valueTable, 0, oldValueTable.length);
|
||||||
super.resize(size);
|
super.resize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object put(Object key, Object value) {
|
public Object put(Object key, Object value) {
|
||||||
int i = add(key);
|
int i = add(key);
|
||||||
Object oldvalue = valueTable[i];
|
Object oldvalue = valueTable[i];
|
||||||
|
@ -71,10 +71,10 @@ public class ObjectMap extends ObjectTable<Object> {
|
||||||
final public Object getAt(int i) {
|
final public Object getAt(int i) {
|
||||||
if (i < 0 || i > currEntry)
|
if (i < 0 || i > currEntry)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return valueTable[i];
|
return valueTable[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
final public Object remove(Object key) {
|
final public Object remove(Object key) {
|
||||||
if (key == null)
|
if (key == null)
|
||||||
return null;
|
return null;
|
||||||
|
@ -84,10 +84,10 @@ public class ObjectMap extends ObjectTable<Object> {
|
||||||
|
|
||||||
Object value = valueTable[i];
|
Object value = valueTable[i];
|
||||||
removeEntry(i);
|
removeEntry(i);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
final protected void removeEntry(int i) {
|
final protected void removeEntry(int i) {
|
||||||
// Remove the entry from the valueTable, shifting everything over if necessary
|
// Remove the entry from the valueTable, shifting everything over if necessary
|
||||||
|
@ -98,14 +98,14 @@ public class ObjectMap extends ObjectTable<Object> {
|
||||||
// Make sure you remove the value before calling super where currEntry will change
|
// Make sure you remove the value before calling super where currEntry will change
|
||||||
super.removeEntry(i);
|
super.removeEntry(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int partition(Comparator<Object> c, int p, int r) {
|
protected int partition(Comparator<Object> c, int p, int r) {
|
||||||
Object x = keyTable[p];
|
Object x = keyTable[p];
|
||||||
Object temp = null;
|
Object temp = null;
|
||||||
int i = p;
|
int i = p;
|
||||||
int j = r;
|
int j = r;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
while (c.compare(keyTable[j], x) > 0) {
|
while (c.compare(keyTable[j], x) > 0) {
|
||||||
j--;
|
j--;
|
||||||
|
@ -115,12 +115,12 @@ public class ObjectMap extends ObjectTable<Object> {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < j) {
|
if (i < j) {
|
||||||
temp = keyTable[j];
|
temp = keyTable[j];
|
||||||
keyTable[j] = keyTable[i];
|
keyTable[j] = keyTable[i];
|
||||||
keyTable[i] = temp;
|
keyTable[i] = temp;
|
||||||
|
|
||||||
temp = valueTable[j];
|
temp = valueTable[j];
|
||||||
valueTable[j] = valueTable[i];
|
valueTable[j] = valueTable[i];
|
||||||
valueTable[i] = temp;
|
valueTable[i] = temp;
|
||||||
|
|
|
@ -18,7 +18,7 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
public abstract class ObjectTable<T> extends HashTable implements Iterable<T> {
|
public abstract class ObjectTable<T> extends HashTable implements Iterable<T> {
|
||||||
protected T[] keyTable;
|
protected T[] keyTable;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -31,14 +31,14 @@ public abstract class ObjectTable<T> extends HashTable implements Iterable<T> {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Object clone() {
|
public Object clone() {
|
||||||
ObjectTable<T> newTable = (ObjectTable<T>) super.clone();
|
ObjectTable<T> newTable = (ObjectTable<T>) super.clone();
|
||||||
|
|
||||||
int size = capacity();
|
int size = capacity();
|
||||||
newTable.keyTable = (T[]) new Object[size];
|
newTable.keyTable = (T[]) new Object[size];
|
||||||
System.arraycopy(keyTable, 0, newTable.keyTable, 0, keyTable.length);
|
System.arraycopy(keyTable, 0, newTable.keyTable, 0, keyTable.length);
|
||||||
|
|
||||||
return newTable;
|
return newTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<T> toList() {
|
public List<T> toList() {
|
||||||
int size = size();
|
int size = size();
|
||||||
List<T> list = new ArrayList<T>(size);
|
List<T> list = new ArrayList<T>(size);
|
||||||
|
@ -51,26 +51,26 @@ public abstract class ObjectTable<T> extends HashTable implements Iterable<T> {
|
||||||
public T keyAt(int i) {
|
public T keyAt(int i) {
|
||||||
if (i < 0 || i > currEntry)
|
if (i < 0 || i > currEntry)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return keyTable[i];
|
return keyTable[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
super.clear();
|
super.clear();
|
||||||
for (int i = 0; i < keyTable.length; i++)
|
for (int i = 0; i < keyTable.length; i++)
|
||||||
keyTable[i] = null;
|
keyTable[i] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final int hash(int pos) {
|
protected final int hash(int pos) {
|
||||||
return hash(keyTable[pos]);
|
return hash(keyTable[pos]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int hash(Object obj) {
|
private int hash(Object obj) {
|
||||||
return obj.hashCode() & ((capacity() * 2) - 1);
|
return hashTable == null ? 0 : hashToOffset(obj.hashCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected void resize(int size) {
|
protected void resize(int size) {
|
||||||
|
@ -79,13 +79,13 @@ public abstract class ObjectTable<T> extends HashTable implements Iterable<T> {
|
||||||
System.arraycopy(oldKeyTable, 0, keyTable, 0, oldKeyTable.length);
|
System.arraycopy(oldKeyTable, 0, keyTable, 0, oldKeyTable.length);
|
||||||
super.resize(size);
|
super.resize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final int add(T obj) {
|
protected final int add(T obj) {
|
||||||
int pos = lookup(obj);
|
int pos = lookup(obj);
|
||||||
if (pos != -1)
|
if (pos != -1)
|
||||||
return pos;
|
return pos;
|
||||||
|
|
||||||
if ((currEntry + 1) >= capacity()) {
|
if (currEntry + 1 >= capacity()) {
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
currEntry++;
|
currEntry++;
|
||||||
|
@ -93,55 +93,55 @@ public abstract class ObjectTable<T> extends HashTable implements Iterable<T> {
|
||||||
linkIntoHashTable(currEntry, hash(obj));
|
linkIntoHashTable(currEntry, hash(obj));
|
||||||
return currEntry;
|
return currEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void removeEntry(int i) {
|
protected void removeEntry(int i) {
|
||||||
// Remove the entry from the keyTable, shifting everything over if necessary
|
// Remove the entry from the keyTable, shifting everything over if necessary
|
||||||
int hash = hash(keyTable[i]);
|
int hash = hash(keyTable[i]);
|
||||||
if (i < currEntry)
|
if (i < currEntry)
|
||||||
System.arraycopy(keyTable, i + 1, keyTable, i, currEntry - i);
|
System.arraycopy(keyTable, i + 1, keyTable, i, currEntry - i);
|
||||||
|
|
||||||
keyTable[currEntry] = null;
|
keyTable[currEntry] = null;
|
||||||
|
|
||||||
// Make sure you remove the value before calling super where currEntry will change
|
// Make sure you remove the value before calling super where currEntry will change
|
||||||
removeEntry(i, hash);
|
removeEntry(i, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final int lookup(Object buffer) {
|
protected final int lookup(Object buffer) {
|
||||||
if (hashTable != null) {
|
if (hashTable != null) {
|
||||||
int hash = hash(buffer);
|
int hash = hash(buffer);
|
||||||
|
|
||||||
if (hashTable[hash] == 0)
|
if (hashTable[hash] == 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
int i = hashTable[hash] - 1;
|
int i = hashTable[hash] - 1;
|
||||||
if (buffer.equals(keyTable[i]))
|
if (buffer.equals(keyTable[i]))
|
||||||
return i;
|
return i;
|
||||||
|
|
||||||
// Follow the next chain
|
// Follow the next chain
|
||||||
for (i = nextTable[i] - 1; i >= 0 && nextTable[i] != i + 1; i = nextTable[i] - 1) {
|
for (i = nextTable[i] - 1; i >= 0 && i != nextTable[i] - 1; i = nextTable[i] - 1) {
|
||||||
if (buffer.equals(keyTable[i]))
|
if (buffer.equals(keyTable[i]))
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
for (int i = 0; i <= currEntry; i++) {
|
for (int i = 0; i <= currEntry; i++) {
|
||||||
if (buffer.equals(keyTable[i]))
|
if (buffer.equals(keyTable[i]))
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsKey(T key) {
|
public boolean containsKey(T key) {
|
||||||
return lookup(key) != -1;
|
return lookup(key) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object[] keyArray() {
|
public Object[] keyArray() {
|
||||||
Object[] keys = new Object[size()];
|
Object[] keys = new Object[size()];
|
||||||
System.arraycopy(keyTable, 0, keys, 0, keys.length);
|
System.arraycopy(keyTable, 0, keys, 0, keys.length);
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <X> X[] keyArray(Class<X> c) {
|
public <X> X[] keyArray(Class<X> c) {
|
||||||
X[] keys = (X[]) Array.newInstance(c, size());
|
X[] keys = (X[]) Array.newInstance(c, size());
|
||||||
|
@ -153,7 +153,7 @@ public abstract class ObjectTable<T> extends HashTable implements Iterable<T> {
|
||||||
if (size() != other.size()) {
|
if (size() != other.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < keyTable.length; i++) {
|
for (int i = 0; i < keyTable.length; i++) {
|
||||||
T key1 = keyTable[i];
|
T key1 = keyTable[i];
|
||||||
T key2 = other.keyTable[i];
|
T key2 = other.keyTable[i];
|
||||||
|
|
Loading…
Add table
Reference in a new issue