1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
6 /** Implementation of an unsynchronized hash table, with one or two
7 * keys, using Radke's quadradic residue linear probing instead of
8 * buckets to minimize object count (less allocations, faster GC).
9 * See C. Radke, Communications of the ACM, 1970, 103-105
13 /** this object is inserted as key in a slot when the
14 * corresponding value is removed -- this ensures that the
15 * probing sequence for any given key remains the same even if
16 * other keys are removed.
18 private static Object placeholder = new Object();
20 /** the number of entries with at least one non-null key */
21 private int usedslots = 0;
23 /** the number of entries with non-null values */
26 /** when num_slots < loadFactor * size, rehash into a bigger table */
27 private final int loadFactor;
30 private Object[] keys1 = null;
32 /** secondary keys; null if no secondary key has ever been added */
33 private Object[] keys2 = null;
35 /** the values for the table */
36 private Object[] vals = null;
38 /** the number of entries with a non-null value */
39 public int size() { return size; }
41 /** empties the table */
45 for(int i=0; i<vals.length; i++) {
48 if (keys2 != null) keys2[i] = null;
52 /** returns all the primary keys in the table */
53 public Object[] keys() {
55 for(int i=0; i<vals.length; i++) if (keys1[i] != null) howmany++;
56 Object[] ret = new Object[howmany];
58 for(int i=0; i<vals.length; i++) if (keys1[i] != null) ret[where++] = keys1[i];
62 public Hash() { this(25, 3); }
63 public Hash(int initialcapacity, int loadFactor) {
64 // using a pseudoprime in the form 4x+3 ensures full coverage
65 initialcapacity = initialcapacity / 4;
66 initialcapacity = 4 * initialcapacity + 3;
67 keys1 = new Object[initialcapacity];
68 vals = new Object[initialcapacity];
69 this.loadFactor = loadFactor;
72 public void remove(Object k1) { remove(k1, null); }
73 public void remove(Object k1, Object k2) { put(k1, k2, null); }
75 private void rehash() {
76 Object[] oldkeys1 = keys1;
77 Object[] oldkeys2 = keys2;
78 Object[] oldvals = vals;
79 keys1 = new Object[oldvals.length * 2];
80 keys2 = oldkeys2 == null ? null : new Object[oldvals.length * 2];
81 vals = new Object[oldvals.length * 2];
84 for(int i=0; i<oldvals.length; i++)
85 if (((oldkeys1[i] != null && oldkeys1[i] != placeholder) || (oldkeys2 != null && oldkeys2[i] != null)) && oldvals[i] != null)
86 put(oldkeys1[i], oldkeys2 == null ? null : oldkeys2[i], oldvals[i]);
89 public Object get(Object k1) { return get(k1, null); }
90 public Object get(Object k1, Object k2) {
91 if (k2 != null && keys2 == null) return null;
92 int hash = (k1 == null ? 0 : k1.hashCode()) ^ (k2 == null ? 0 : k2.hashCode());
93 int dest = Math.abs(hash) % vals.length;
97 while (keys1[dest] != null || (keys2 != null && keys2[dest] != null)) {
98 Object hk1 = keys1[dest];
99 Object hk2 = keys2 == null ? null : keys2[dest];
100 if ((k1 == hk1 || (k1 != null && hk1 != null && k1.equals(hk1))) &&
101 (k2 == hk2 || (k2 != null && hk2 != null && k2.equals(hk2)))) {
104 dest = Math.abs((odest + (plus ? 1 : -1 ) * tries * tries) % vals.length);
111 public void put(Object k1, Object v) { put(k1, null, v); }
112 public void put(Object k1, Object k2, Object v) {
113 if (usedslots * loadFactor > vals.length) rehash();
114 int hash = (k1 == null ? 0 : k1.hashCode()) ^ (k2 == null ? 0 : k2.hashCode());
115 int dest = Math.abs(hash) % vals.length;
120 Object hk1 = keys1[dest];
121 Object hk2 = keys2 == null ? null : keys2[dest];
122 if (hk1 == null && hk2 == null) { // empty slot
123 if (v == null) return;
129 if ((k1 == hk1 || (k1 != null && hk1 != null && k1.equals(hk1))) && // replacing former entry
130 (k2 == hk2 || (k2 != null && hk2 != null && k2.equals(hk2)))) {
132 // we don't actually remove things from the table; rather, we insert a placeholder
141 dest = Math.abs((odest + (plus ? 1 : -1 ) * tries * tries) % vals.length);
147 if (k2 != null && keys2 == null) keys2 = new Object[keys1.length];
148 if (keys2 != null) keys2[dest] = k2;