-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHashDictionary.java
276 lines (196 loc) · 9.07 KB
/
HashDictionary.java
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
// package cheatdetect;
// A dictionary based on a hash table that implements the Dictionary interface
public class HashDictionary implements Dictionary {
private Entry[] hashTable; // Hash table
private HashCode code; // Hash code
private int N = 7; // Size of hash table
private int count = 0; // Number of entries in dictionary
private float maxLoadFactor; // User-specified maximum allotment for load factor
private int opsCount; // Number of find, insert & remove operations have been performed
private int probeCount; // Number of probes that have occurred
// Empty entry for to replace deleted entries
private Entry AVAILABLE = new Entry(null, new Pair(-1,-1));
// The following variables are used between methods & changed during rehashing
private int a = 0; // MAD compression variables
private int b = -1;
private int q = 5; // Arbitrary prime value < N used for double hashing technique
/* CONSTRUCTORS */
// Default constructor
public HashDictionary() throws DictionaryException {
// Throws DictionaryException with a specified detail message
throw new DictionaryException("Please specify a hash code and a maximum load factor.");
}
// Constructor
public HashDictionary(HashCode inputCode, float inputLoadFactor) {
// Assign values to attributes
hashTable = new Entry[N];
this.code = inputCode;
this.maxLoadFactor = inputLoadFactor;
// Set random coefficients a and b for MAD compression
// Coefficient a cannot be 0 or a factor of N
while ( a == 0 || a % N == 0)
a = (int)(Math.random()*Integer.MAX_VALUE);
// Coefficient b must be positive
while (b < 0)
b = (int)(Math.random()*Integer.MAX_VALUE);
}
/* PRINCIPAL METHODS */
// Inserts the entry with the specified key and value to the dictionary.
public void insert(String k, Pair v){
// Increment number of operations performed thus far
opsCount++;
// Resize & rehash if load factor exceeds maximum load factor
if (getLoadFactor() >= maxLoadFactor)
rehash();
// Initially, stepSize = 0 so only MAD compression is applied
int stepSize = 0;
while (stepSize < N) {
// Increment number of probes performed thus far
probeCount++;
// Get hashed index
int i = getHashedIndex(code.giveCode(k),stepSize);
// If index contents are empty or available, new entry can be inserted
if ((hashTable[i] == null) || (hashTable[i] == AVAILABLE)) {
// Add new entry
hashTable[i] = new Entry(k, v);
// Increment number of entries in hash table
count++;
break;
}
// If index is unavailable (collision), increment step size and re-calculate new index position (via double hashing)
else
stepSize++;
}
}
// Removes the entry with the specified key from the dictionary. Throws
// DictionaryException if no entry with key in the dictionary
public Entry remove(String k) throws DictionaryException {
// Increment number of operations performed thus far
opsCount++;
// Initially, stepSize = 0 & index is produced by MAD compression only (no double hashing)
int stepSize = 0;
while (stepSize < N) {
// Increment number of probes performed thus far
probeCount++;
// Get hashed index
int i = getHashedIndex(code.giveCode(k),stepSize);
// If index is empty, entry does not exist and cannot be removed
if (hashTable[i] == null)
throw new DictionaryException("This entry does not exist.");
// If index is AVAILABLE, skip key comparison and move to next possible entry index
// If index is empty and unavailable, check for key match
else if (hashTable[i] != AVAILABLE) {
if (hashTable[i].Key().equals(k)) {
// Store entry
Entry temp = hashTable[i];
// Replace contents with 'AVAILABLE'
hashTable[i] = AVAILABLE;
// Decrement number of entries in hash table
count--;
// Return entry that was once there
return temp;
}
}
//If entry has not yet been found, increment step size and re-calculate new index position (via double hashing)
stepSize++;
}
// If entry was not found, throw an exception
throw new DictionaryException("This entry does not exist.");
}
// If entry with this key exists in the dictionary, returns this entry
// Otherwise returns null
public Entry find(String k) {
// Increment number of operations performed thus far
opsCount++;
// Initially, stepSize = 0 so only MAD compression is applied (no double hashing)
int stepSize = 0;
while (stepSize < N) {
// Increment number of probes performed thus far
probeCount++;
// Get hashed index; first return value will be index after MAD compression only (since stepSize = 0)
int i = getHashedIndex(code.giveCode(k), stepSize);
// If index's contents are null, assume entry was never inserted in table
if(hashTable[i] == null)
return null;
// If index's contents represent a non-empty entry, check for a key match
if (hashTable[i] != AVAILABLE) {
if ((hashTable[i].Key()).equals(k))
return hashTable[i];
}
// If key was not found, increase step size and move to next possible insertion point
stepSize++;
}
// If no key was found, return null
return null;
}
// Returns number of entries in hash table
public int size() {
return count;
}
// Returns average number of probes performed per operation
public float averNumProbes() {
return (float) probeCount/opsCount;
}
/* ADDITIONAL METHODS */
// Applies MAD compression, as well as double hashing when collisions occur (open addressing)
private int getHashedIndex(int hashCode, int i) {
// Applies MAD compression (ie. h(k) function)
int h1 = (Math.abs(a * hashCode + b)) % N;
// Suggested h'(k) function
int h2 = q - Math.abs(h1) % q;
// When stepsize (i) = 0, MAD compression alone is applied
// Returns hashed index
return (h1 + i * h2) % N;
}
// Invoked when load factor exceeds maximum load factor intially specified by user through constructor
private void rehash() {
// Reset hash table count
count = 0;
// Replace variable used in 'getHashedIndex' with old capacity
q = N;
// Resize hash table to be greater than double the current capacity; new size must be prime
int x = 1;
N *= 2;
while (isPrime(N + x) == false)
x += 2; // To avoid even numbers
// Store new hash table size
N += x;
// Recalculate coefficients a and b for MAD compression function (ensuring a % new size == 0)
// Coefficient a cannot be 0 or a factor of the table's size
while ( a == 0 || a % N == 0)
a = (int)(Math.random()*Integer.MAX_VALUE);
// Coefficient b must be positive
while (b < 0)
b = (int)(Math.random()*Integer.MAX_VALUE);
// Store current entries in a temporary table
Entry [] temp = new Entry[q];
for (int i = 0; i < q; i++) {
// Copy indicies with nonempty contents
if ((hashTable[i] != null) && (hashTable[i] != AVAILABLE))
temp[i] = hashTable[i];
}
// Resize hash table to new capacity
hashTable = new Entry[N];
// Re-insert keys and pair values from temporary table into new hash table
for (int i = 0; i < q; i++) {
if(temp[i] != null)
insert(temp[i].Key(), temp[i].Value());
}
}
// Calculates current load factor
private float getLoadFactor() {
return (float) count/N;
}
// Calculates if integer is prime
private boolean isPrime(int n) {
// Test for even integers
if (n % 2 == 0)
return false;
// Test other possible divisors/factors up to sqrt(n)
for (int i = 3; i*i <= n; i += 2)
if (n % i == 0)
return false;
// If n is not divisible by any integers <= sqrt(n), then it must be prime
return true;
}
}