-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathHashTable.java
More file actions
338 lines (300 loc) · 9.08 KB
/
HashTable.java
File metadata and controls
338 lines (300 loc) · 9.08 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
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
/**
* Programmer: John Kurlak
* Last Modified: 11.8.2010
* Purpose: To implement a generic hash table
*/
// Import
import java.util.Vector;
/**
* This class represents a generic hash table.
*
* @author John Kurlak
* @date 11.8.2010
* @param <K> Any generic type representing the key for the hash table; this
* must implement toString() in a unique manner for unique keys
* @param <V> Any generic type representing the values for the hash table
*/
public class HashTable<K, V>
{
/**
* This class represents an entry (or collection of entries) in the hash
* table.
*
* @author John Kurlak
* @param <K> Any generic type representing the key for the hash table;
* this must implement toString() in a unique manner for unique
* keys
* @param <V> Any generic type representing the values for the hash table
*/
private static class HashEntry<K, V>
{
private K key;
private Vector<V> value = new Vector<V>();
private boolean isTombstone = false;
/**
* This constructor creates a new hash entry at the given key with the
* given value.
*
* @param insertKey The key for the entry
* @param insertValue The value for the entry
*/
private HashEntry(K insertKey, V insertValue)
{
key = insertKey;
value.add(insertValue);
}
/**
* This method makes a hash entry into a "tombstone" (a deleted entry)
*/
public void makeTombstone()
{
isTombstone = true;
}
}
private final int[] SIZES = { 1019, 2027, 4079, 8123, 16267, 32503, 65011,
130027, 260111, 520279, 1040387, 2080763, 4161539, 8323151, 16646323 };
private int sizeIdx = 0;
private HashEntry<K, V>[] table;
private int numEntries = 0;
private int numFilledSlots = 0;
private int numProbes = 0;
/**
* This method creates a new hash table of the next size.
*/
@SuppressWarnings("unchecked")
public HashTable()
{
table = new HashEntry[SIZES[sizeIdx]];
}
/**
* This method increases the hash table's size to the next available size.
*/
@SuppressWarnings("unchecked")
private void increaseCapacity()
{
// Store a reference to the old table
HashEntry<K, V>[] oldTable = table;
// Attempt to resize the table
try
{
// Make a new table full of empty entries
table = new HashEntry[SIZES[++sizeIdx]];
}
// We have too many entries in the hash table: no more sizes left
catch (ArrayIndexOutOfBoundsException e)
{
System.out.println("Too many entries in hash table. Exiting.");
System.exit(4);
}
for (int i = 0; i < oldTable.length; ++i)
{
// If we are at an entry with a key and value
if (oldTable[i] != null && !oldTable[i].isTombstone)
{
// Add every value at that key to the bigger table
for (V value : oldTable[i].value)
{
insert(oldTable[i].key, value);
}
}
}
}
/**
* This method inserts a value into the hash table with the given key.
*
* @param key The key with which to associate the value
* @param value The value to insert
* @return True if the insertion succeeded and false if it did not
*/
public boolean insert(K key, V value)
{
int size = SIZES[sizeIdx];
int i;
numProbes = 0;
// Make sure we haven't filled 70% or more of the entries
if (numFilledSlots >= 0.7 * size)
{
// If we're running out of space, increase the size of our table
increaseCapacity();
size = SIZES[sizeIdx];
}
// Probe no more iterations than the size of the table
for (i = 0; i < size; ++i)
{
// Compute the next index in the probe sequence
int index = probe(key, i, size);
// If the current slot doesn't contain a value
if (table[index] == null || table[index].isTombstone)
{
// Store the given value at the current slot
table[index] = new HashEntry<K, V>(key, value);
++numEntries;
++numFilledSlots;
numProbes = i;
return true;
}
// If the current slot has a value with the same key as the key we
// are inserting with
else if (table[index].key.equals(key) && !table[index].isTombstone)
{
// Add the given value to the current slot
table[index].value.add(value);
++numEntries;
numProbes = i;
return true;
}
}
// Compute the number of probes if probing failed
numProbes = i - 1;
// The value wasn't inserted because we couldn't find a place to insert
// it
return false;
}
/**
* This method returns the next probe value.
*
* @param key The key to probe for
* @param i The current probe index
* @param size The current size of the hash table
* @return The next probe value
*/
private int probe(K key, int i, int size)
{
// Use quadratic probing of the form: (i^2 + i) / 2
return (hash(key) + ((int) (Math.pow(i, 2) + i) >> 2)) % size;
}
/**
* This method returns the maximum number of probes that were necessary in
* the most recent find() call.
*
* @return The number of maximum number of probes that were necessary in
* the most recent find() call
*/
public int getNumProbes()
{
return numProbes;
}
/**
* This method finds the value(s) stored at the given key.
*
* @param key The key to find the value(s) for
* @return A vector of values that lie at the given key
*/
public Vector<V> find(K key)
{
int size = SIZES[sizeIdx];
// Probe no more iterations than the size of the table
for (int i = 0; i < size; ++i)
{
// Compute the next index in the probe sequence
int index = probe(key, i, size);
// If we reach an empty slot, we know the key isn't in the table
if (table[index] == null)
{
return null;
}
// If we find the key and it isn't a tombstone
else if (table[index].key.equals(key) && !table[index].isTombstone)
{
// Return the vector of values for that slot
return table[index].value;
}
}
// If we've been probing for a long time, the key probably isn't in the
// table
return null;
}
/**
* This method deletes all of the values stored at a particular key value.
*
* @param key The key value corresponding to the records that we want
* to delete
* @return True if the deletion was successful and false if it wasn't
*/
public boolean delete(K key)
{
int size = SIZES[sizeIdx];
// Probe no more iterations than the size of the table
for (int i = 0; i < size; ++i)
{
// Compute the next index in the probe sequence
int index = probe(key, i, size);
// If we reach an empty slot, we know the key isn't in the table
if (table[index] == null)
{
return false;
}
// If we find the key and it isn't a tombstone
else if (table[index].key.equals(key) && !table[index].isTombstone)
{
// Make it a tombstone
table[index].isTombstone = true;
return true;
}
}
// If we've been probing for a long time, the key probably isn't in the
// table
return false;
}
/**
* This method hashes a key into an integer using the ELF hash algorithm.
*
* @pre The key must implement the toString() method in a way such that
* it returns distinct strings for distinct keys
* @post The return value will return a balanced distribution of hashes
* among successive calls with different keys
* @param key The key to hash
* @return An integer representing the hashed version of the key
*/
private int hash(K key)
{
String toHash = key.toString();
int hashValue = 0;
for (int pos = 0; pos < toHash.length(); ++pos)
{
// Compute a hash value for the current letter
hashValue = (hashValue << 4) + toHash.charAt(pos);
int highBits = hashValue & 0xF0000000;
if (highBits != 0)
{
hashValue ^= highBits >> 24;
}
hashValue &= ~highBits;
}
return hashValue;
}
/**
* This method prints debug information for the hash table to the log.
*
* @pre The hash table has values in it
* @post The log file will have debug information about the hash table
*/
public void debug()
{
float entriesPerSlot = (float) numEntries / (float) numFilledSlots;
String result = "Format of display is\n";
result += "Slot number: data record\n\n";
result += "Current table size:\t\t\t\t\t\t" + table.length + "\n";
result += "Number of elements in table:\t\t\t" + numEntries + "\n";
result += "Number of filled slots in table:\t\t" + numFilledSlots + "\n";
result += "Average number of entries per slot is:\t" + entriesPerSlot + "\n";
System.out.println(result);
for (int i = 0; i < table.length; i++)
{
// If the current slot has a value in it
if (table[i] != null && !table[i].isTombstone)
{
// Store the key that it stores
result = "\n" + i + ":\t" + ((i < 100) ? "\t" : "") + "[" + table[i].key.toString() + ", ";
// Loop through all of the entries at that key
for (V entry : table[i].value)
{
// Store the next value at that key
result += "(" + entry.toString() + "), ";
}
result = result.substring(0, result.length() - 2) + "]";
System.out.println(result);
}
}
}
}