5 |
irasnyd |
1 |
//import chap09.list03.Map;
|
108 |
ira |
2 |
// License: Public Domain (added on 07-11-2005)
|
5 |
irasnyd |
3 |
import java.io.*;
|
|
|
4 |
import java.util.*;
|
|
|
5 |
|
|
|
6 |
public class HashTable implements Map { // open addressing
|
7 |
irasnyd |
7 |
private Entry[] entries;
|
|
|
8 |
|
|
|
9 |
//size is the total number of elements, including NIL
|
|
|
10 |
//used is the total number of elements, NOT INCLUDING NIL
|
5 |
irasnyd |
11 |
private int size, used;
|
|
|
12 |
private float loadFactor;
|
|
|
13 |
private final Entry NIL = new Entry(null,null); // dummy
|
9 |
irasnyd |
14 |
|
10 |
irasnyd |
15 |
private int probeType; //holds the type of probing to use
|
|
|
16 |
|
9 |
irasnyd |
17 |
//variable to hold total number of collisions this run.
|
|
|
18 |
//Definition: collision - the number of times we must jump to
|
|
|
19 |
// get to the next null space in the table
|
|
|
20 |
// This includes jumping NIL's
|
|
|
21 |
//
|
|
|
22 |
//NOTE: will only be counted on inserting values
|
7 |
irasnyd |
23 |
private int collisions; //initialized to 0 by default
|
5 |
irasnyd |
24 |
|
7 |
irasnyd |
25 |
//create a HashTable with a certain capacity (maximum items)
|
|
|
26 |
//and a maximum load factor (if we exceed this we will rehash)
|
5 |
irasnyd |
27 |
public HashTable(int capacity, float loadFactor) {
|
|
|
28 |
entries = new Entry[capacity];
|
|
|
29 |
this.loadFactor = loadFactor; }
|
|
|
30 |
|
7 |
irasnyd |
31 |
//create a HashTable with a certain capacity (maximum items)
|
|
|
32 |
//and a default load factor of 0.75
|
5 |
irasnyd |
33 |
public HashTable(int capacity) {
|
7 |
irasnyd |
34 |
this(capacity,0.75F);} //calls the HashTable( int, float ) constructor
|
5 |
irasnyd |
35 |
|
7 |
irasnyd |
36 |
//create a default HashTable with default capacity (101 items) and
|
|
|
37 |
//default load factor of 0.75
|
5 |
irasnyd |
38 |
public HashTable() {
|
7 |
irasnyd |
39 |
this(101); } //calls the HashTable( int ) constructor
|
5 |
irasnyd |
40 |
|
7 |
irasnyd |
41 |
//Method to find a certain key in the HashTable
|
|
|
42 |
//Precondition: key != null
|
|
|
43 |
//Postcondition: return the entry's value if the key is found
|
|
|
44 |
// otherwise return null for not found
|
5 |
irasnyd |
45 |
public Object get(Object key) {
|
|
|
46 |
int h=hash(key);
|
|
|
47 |
for (int i=0; i<entries.length; i++) {
|
|
|
48 |
int j = nextProbe(h,i);
|
|
|
49 |
Entry entry=entries[j];
|
|
|
50 |
if (entry==null) break;
|
|
|
51 |
if (entry==NIL) continue;
|
|
|
52 |
if (entry.key.equals(key)) return entry.value; // success
|
|
|
53 |
}
|
|
|
54 |
return null; // failure: key not found
|
|
|
55 |
}
|
6 |
irasnyd |
56 |
|
7 |
irasnyd |
57 |
//Method to add a key/value pair to the HashTable
|
|
|
58 |
//Key - the value that gets hash() called on it. Should be unique (hopefully)
|
|
|
59 |
//Value - the actual data that is to be stored.
|
|
|
60 |
//Precondition: key != null
|
|
|
61 |
//Postcondition: return null if the object was inserted sucessfully
|
|
|
62 |
// return the value that was there if we updated a value
|
|
|
63 |
// return null if the table overflows (should never happen,
|
|
|
64 |
// but it is there just in case)
|
5 |
irasnyd |
65 |
public Object put(Object key, Object value) {
|
|
|
66 |
if (used>loadFactor*entries.length) rehash();
|
11 |
irasnyd |
67 |
|
16 |
irasnyd |
68 |
System.out.print(key); //print out the key
|
|
|
69 |
|
|
|
70 |
int h=hash(key); //hash the key
|
11 |
irasnyd |
71 |
|
21 |
irasnyd |
72 |
//run once for each position in the Table
|
|
|
73 |
for (int i=0; i<entries.length; i++) {
|
|
|
74 |
//get the [first, next] place to try putting the value
|
|
|
75 |
int j = nextProbe(h,i);
|
|
|
76 |
//get the Entry at j (where we want to put value)
|
|
|
77 |
Entry entry=entries[j];
|
7 |
irasnyd |
78 |
|
21 |
irasnyd |
79 |
//print the location we tried to place the value
|
|
|
80 |
System.out.print(" -> " + j);
|
16 |
irasnyd |
81 |
|
|
|
82 |
//if entry[j] == null then we have found a good place to put value!
|
|
|
83 |
if (entry==null) {
|
|
|
84 |
|
11 |
irasnyd |
85 |
System.out.println(); //terminate the line
|
16 |
irasnyd |
86 |
|
5 |
irasnyd |
87 |
entries[j] = new Entry(key,value);
|
|
|
88 |
++size;
|
|
|
89 |
++used;
|
|
|
90 |
return null; // insertion success
|
|
|
91 |
}
|
16 |
irasnyd |
92 |
|
|
|
93 |
//if entry[j] == NIL then we must continue to probe for a new place,
|
|
|
94 |
//otherwise we will confuse the remove() and get() methods
|
9 |
irasnyd |
95 |
if (entry==NIL) { collisions++; continue; }
|
7 |
irasnyd |
96 |
|
16 |
irasnyd |
97 |
//if entry[j].key == key then we need to update the record
|
5 |
irasnyd |
98 |
if (entry.key.equals(key)) {
|
|
|
99 |
Object oldValue=entry.value;
|
|
|
100 |
entries[j].value = value;
|
|
|
101 |
return oldValue; // update success
|
|
|
102 |
}
|
9 |
irasnyd |
103 |
|
16 |
irasnyd |
104 |
//if we get here, we have had a collision that is not a NIL,
|
|
|
105 |
//and is not an Entry that needs updating, therefore it is something
|
|
|
106 |
//else that hashes to the same value. Therefore we must increment the
|
|
|
107 |
//collision counter
|
|
|
108 |
collisions++;
|
|
|
109 |
|
|
|
110 |
} //end for loop
|
|
|
111 |
|
|
|
112 |
return null; // failure: table overflow
|
|
|
113 |
} //end put()
|
5 |
irasnyd |
114 |
|
21 |
irasnyd |
115 |
//Method to remove a key/value pair from the table.
|
|
|
116 |
//This will leave a NIL behind
|
7 |
irasnyd |
117 |
//Precondition: key != null
|
21 |
irasnyd |
118 |
//Postcondition: return the old value if the key was found, then
|
|
|
119 |
// put a NIL in it's place. return null if the key
|
|
|
120 |
// was not found in the table.
|
5 |
irasnyd |
121 |
public Object remove(Object key) {
|
7 |
irasnyd |
122 |
int h=hash(key); //hash the key
|
21 |
irasnyd |
123 |
//run once for every place in the table
|
|
|
124 |
for (int i=0; i<entries.length; i++) {
|
|
|
125 |
//find the [first, next] place to put the value
|
|
|
126 |
int j = nextProbe(h,i);
|
5 |
irasnyd |
127 |
Entry entry=entries[j];
|
21 |
irasnyd |
128 |
|
|
|
129 |
//break out of the loop, since the key was not found
|
|
|
130 |
if (entry==null) break;
|
7 |
irasnyd |
131 |
if (entry==NIL) continue; //keep going, since we found a NIL
|
|
|
132 |
if (entry.key.equals(key)) { //we found the key we are looking for...
|
|
|
133 |
Object oldValue=entry.value; //save value to return
|
|
|
134 |
entries[j] = NIL; //replace the value with a NIL
|
|
|
135 |
--size; //decrement size
|
5 |
irasnyd |
136 |
return oldValue; // success
|
|
|
137 |
}
|
|
|
138 |
}
|
|
|
139 |
return null; // failure: key not found
|
|
|
140 |
}
|
|
|
141 |
|
7 |
irasnyd |
142 |
//Method to access private variable size
|
|
|
143 |
//Precondition: none
|
|
|
144 |
//Postcondition: return size
|
5 |
irasnyd |
145 |
public int size() {
|
|
|
146 |
return size;
|
|
|
147 |
}
|
|
|
148 |
|
9 |
irasnyd |
149 |
//Method to access private variable collisions
|
|
|
150 |
//Precondition: none
|
|
|
151 |
//Postcondition: return collisions
|
|
|
152 |
public int collisions() {
|
16 |
irasnyd |
153 |
return collisions;
|
9 |
irasnyd |
154 |
}
|
|
|
155 |
|
10 |
irasnyd |
156 |
//Method to set the probeType variable
|
|
|
157 |
//
|
|
|
158 |
//0 == Linear Probing (step size 1) DEFAULT
|
|
|
159 |
//1 == Prime Probing (step size 3)
|
|
|
160 |
//2 == Prime Probing (step size 5)
|
|
|
161 |
//3 == Prime Probing (step size 7)
|
|
|
162 |
//4 == Prime Probing (step size 11)
|
|
|
163 |
//5 == Quadratic Probing
|
|
|
164 |
//6 == Double Hashing
|
|
|
165 |
//
|
|
|
166 |
//Precondition: 0 <= newValue <= 6
|
|
|
167 |
//Postcondition: probeType will be changed
|
|
|
168 |
//WARNING: Please do NOT change probeType while in a test run
|
|
|
169 |
public void setProbeType( int newValue ) {
|
21 |
irasnyd |
170 |
//put in for safety, just in case
|
|
|
171 |
if( newValue < 0 || newValue > 6 ) { newValue = 0; }
|
16 |
irasnyd |
172 |
probeType = newValue;
|
10 |
irasnyd |
173 |
}
|
|
|
174 |
|
7 |
irasnyd |
175 |
//A private class which will hold a key/value pair
|
5 |
irasnyd |
176 |
private class Entry {
|
|
|
177 |
Object key, value;
|
|
|
178 |
Entry(Object k, Object v) { key=k; value=v; }
|
|
|
179 |
}
|
|
|
180 |
|
7 |
irasnyd |
181 |
//our implementation of the hash() function. Takes the Object.hashCode() method
|
|
|
182 |
//and chops off the sign bit
|
|
|
183 |
//Precondition: None (we check for key == null)
|
|
|
184 |
//Postcondition: throw an IllegalArgumentException() if the key == null
|
|
|
185 |
// return an integer hash for the given key
|
5 |
irasnyd |
186 |
private int hash(Object key) {
|
|
|
187 |
if (key==null) throw new IllegalArgumentException();
|
|
|
188 |
return (key.hashCode() & 0x7FFFFFFF) % entries.length;
|
|
|
189 |
}
|
7 |
irasnyd |
190 |
|
|
|
191 |
//Method to implement linear probing
|
|
|
192 |
//Precondition: h != null; i != null
|
|
|
193 |
//Postcondition: return the next place to try and put the value
|
5 |
irasnyd |
194 |
private int nextProbe(int h, int i) {
|
10 |
irasnyd |
195 |
|
16 |
irasnyd |
196 |
int returnVal = 0; //set to zero to stop compiler error
|
8 |
irasnyd |
197 |
|
16 |
irasnyd |
198 |
switch( probeType ) {
|
17 |
irasnyd |
199 |
case 0: //linear probing
|
16 |
irasnyd |
200 |
returnVal = (h + i)%entries.length;
|
|
|
201 |
break;
|
17 |
irasnyd |
202 |
case 1: //Prime Probing ( p=3 )
|
16 |
irasnyd |
203 |
returnVal = (h + (i * 3 ))%entries.length;
|
|
|
204 |
break;
|
17 |
irasnyd |
205 |
case 2: //Prime Probing ( p=5 )
|
16 |
irasnyd |
206 |
returnVal = (h + (i * 5 ))%entries.length;
|
|
|
207 |
break;
|
17 |
irasnyd |
208 |
case 3: //Prime Probing ( p=7 )
|
16 |
irasnyd |
209 |
returnVal = (h + (i * 7 ))%entries.length;
|
|
|
210 |
break;
|
17 |
irasnyd |
211 |
case 4: //Prime Probing ( p=11 )
|
16 |
irasnyd |
212 |
returnVal = (h + (i * 11))%entries.length;
|
|
|
213 |
break;
|
17 |
irasnyd |
214 |
case 5: //Quadratic Probing
|
16 |
irasnyd |
215 |
returnVal = (h + (i * i ))%entries.length;
|
|
|
216 |
break;
|
|
|
217 |
//I am aware that my implementation of Double Hashing (which follows) is
|
|
|
218 |
//wasteful in terms of computation time (we must create the array "step"
|
|
|
219 |
//each and every time this code is run. I did it this way to abstract the
|
|
|
220 |
//method used to find the nextProbe() location from the put method.
|
17 |
irasnyd |
221 |
case 6: //Double Hashing
|
16 |
irasnyd |
222 |
int[] step = { 2,3,5,7,11 };
|
|
|
223 |
int stepSize = step[h%4];
|
|
|
224 |
returnVal = (h + (i * stepSize))%entries.length;
|
|
|
225 |
break;
|
|
|
226 |
}
|
|
|
227 |
//return the final value
|
|
|
228 |
return returnVal;
|
8 |
irasnyd |
229 |
|
5 |
irasnyd |
230 |
}
|
|
|
231 |
|
7 |
irasnyd |
232 |
//Method to rehash the entire table. This will get rid of NIL's
|
|
|
233 |
//Precondition: none
|
|
|
234 |
//Postcondition: entries will be twice the size plus one (ensures odd size)
|
|
|
235 |
// all NIL will be gone
|
|
|
236 |
// all values will be re-hash()ed
|
5 |
irasnyd |
237 |
private void rehash() {
|
|
|
238 |
Entry[] oldEntries = entries;
|
|
|
239 |
entries = new Entry[2*oldEntries.length+1];
|
|
|
240 |
for (int k=0; k<oldEntries.length; k++) {
|
|
|
241 |
Entry entry=oldEntries[k];
|
|
|
242 |
if (entry==null || entry==NIL) continue;
|
|
|
243 |
int h=hash(entry.key);
|
|
|
244 |
for (int i=0; i<entries.length; i++) {
|
|
|
245 |
int j = nextProbe(h,i);
|
|
|
246 |
if (entries[j]==null) {
|
|
|
247 |
entries[j] = entry;
|
|
|
248 |
break;
|
|
|
249 |
}
|
|
|
250 |
}
|
|
|
251 |
}
|
|
|
252 |
used = size;
|
|
|
253 |
}
|
|
|
254 |
}
|
18 |
irasnyd |
255 |
|