-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathCuckooHashImpl.java
More file actions
271 lines (245 loc) · 6.64 KB
/
CuckooHashImpl.java
File metadata and controls
271 lines (245 loc) · 6.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Random;
/**
* Cuckoo hashing allows closer to perfect hashing by using 2 different hashcodes
* that allow 2 positions in the table for each key-value pair
*
* @author Cam
* @version 1.0, 7/20/2012
*/
public class CuckooHashImpl<K, V> {
private int CAPACITY;
private int a = 37, b = 17;
private Bucket<K, V>[] table;
/**
* test meth
*/
public static void main(String[] args) {
CuckooHashImpl<String, String> table = new CuckooHashImpl<String, String>(10);
table.put("A", "AA");
System.out.println(table.toString() + " " + table.size());
table.put("A", "LL");
System.out.println(table.toString() + " " + table.size());
table.put("B", "BB");
System.out.println(table.toString() +" " + table.size());
table.put("C", "CC");
System.out.println(table.toString() +" " + table.size());
table.put("C", "HH");
System.out.println(table.toString() +" " + table.size());
table.put("A", "CC");
System.out.println(table.toString() +" " + table.size());
table.put("S", "SS");
System.out.println(table.toString() +" " + table.size());
table.put("A", "AA");
System.out.println(table.toString() +" " + table.size());
System.out.println();
System.out.println("KEYS: " + table.keys());
System.out.println("VALUES: " + table.values());
System.out.println();
table.remove("A", "AA");
table.remove("A", "CC");
System.out.println(table.toString() +" " + table.size());
//table.rehash();
//System.out.println(table.toString());
table.clear();
System.out.println(table.toString() +" " + table.size());
}
/**
* Inner bucket class
* @param <K> - type of key
* @param <V> - type of value
*/
private class Bucket<K, V> {
private K bucKey = null;
private V value = null;
public Bucket(K k, V v) {
bucKey = k;
value = v;
}
/**
* Getters and Setters
*/
private K getBucKey() {
return bucKey;
}
//private void setBucKey(K key) {
//bucKey = key;
//}
private V getValue() {
return value;
}
//private void setValue(V value) {
//this.value = value;
//}
}
/**
* a two parameter constructor that sets capacity and the loadfactor limit
* @param size user input multimap capacity
* @param lf user input load factor limit
*/
public CuckooHashImpl (int size) {
CAPACITY = size;
table = new Bucket[CAPACITY];
}
/**
* Get the number of elements in the table
* @return total key-value pairs
*/
public int size() {
int count = 0;
for (int i=0; i<CAPACITY; ++i) {
if (table[i] != null)
count++;
}
return count;
}
/**
* Removes all elements in the table
*/
public void clear() {
table = new Bucket[CAPACITY];
}
/**
* Get a list of all values in the table
* @return the values as a list
*/
public List<V> values() {
List<V> allValues = new ArrayList<V>();
for (int i=0; i<CAPACITY; ++i) {
if (table[i] != null) {
allValues.add(table[i].getValue());
}
}
return allValues;
}
/**
* Get all the keys in the table
* @return a set of keys
*/
public Set<K> keys() {
Set<K> allKeys = new HashSet<K>();
for (int i=0; i<CAPACITY; ++i) {
if (table[i] != null) {
allKeys.add(table[i].getBucKey());
}
}
return allKeys;
}
/**
* Adds a key-value pair to the table by means of cuckoo hashing.
* Insert into either of 2 separate hash indices, if both are full loop to
* next hashed index and displace the key-value pair there, else loop for
* n = size times.
* @param key the key of the element to add
* @param value the value of the element to add
*/
public void put(K key, V value) {
int index = key.hashCode() % CAPACITY;
int index2 = altHashCode(key) % CAPACITY;
if (table[index] != null && table[index].getValue().equals(value))
return;
if (table[index2] != null && table[index2].getValue().equals(value))
return;
int pos = index;
Bucket<K, V> buck = new Bucket<K, V>(key, value);
for (int i=0; i<3; i++) {
if (table[pos] == null) {
table[pos] = buck;
return;
}
else {
Bucket<K, V> copy = table[pos];
table[pos] = buck;
buck = copy;
}
if (pos == index)
pos = index2;
else
pos = index;
}
rehash();
put(key, value);
}
/**
* Retrieve a value in O(1) time based on the key b/c it can only be in 1 of 2 locations
* @param key Key to search for
* @return the found value or null if it doesn't exist
*/
public V get(K key) {
int index = key.hashCode() % CAPACITY;
int index2 = altHashCode(key) % CAPACITY;
if (table[index] != null && table[index].getBucKey().equals(key))
return table[index].getValue();
else if (table[index2] != null && table[index2].getBucKey().equals(key))
return table[index2].getValue();
return null;
}
/**
* Secondary custom hashcoder for cuckoo hashing
* @param key The key to be hashed
* @return hashcode
*/
public int altHashCode(K key) {
return a * b + key.hashCode();
}
/**
* Removes this key value pair from the table
* @param key the key to remove
* @param value the value to remove
* @return successful removal
*/
public boolean remove(K key, V value) {
int index = key.hashCode() % CAPACITY;
int index2 = altHashCode(key) % CAPACITY;
if (table[index] != null && table[index].getValue().equals(value)) {
table[index] = null;
return true;
}
else if (table[index2] != null && table[index2].getValue().equals(value)) {
table[index2] = null;
return true;
}
return false;
}
/**
* String representaiton of the table
* @return the table's contents
*/
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[ ");
for (int i=0; i<CAPACITY; ++i) {
if (table[i] != null) {
sb.append("<");
sb.append(table[i].getBucKey()); //key
sb.append(", ");
sb.append(table[i].getValue()); //value
sb.append("> ");
}
}
sb.append("]");
return sb.toString();
}
/**
* a method that regrows the hashtable to capacity: 2*old capacity + 1 and
* reinserts all the key->value pairs.
*/
public void rehash() {
Bucket<K, V>[] tableCopy = table.clone();
int OLD_CAPACITY = CAPACITY;
CAPACITY = (CAPACITY * 2) + 1;
table = new Bucket[CAPACITY];
for (int i=0; i<OLD_CAPACITY; ++i) {
if (tableCopy[i] != null) {
put(tableCopy[i].getBucKey(), tableCopy[i].getValue());
}
}
//reset alt hash func
Random gen = new Random();
a = gen.nextInt(37);
b = gen.nextInt(17);
}
}