|
1 | 1 | package _7_12_Hash_Table; |
2 | 2 |
|
3 | | -import java.util.ArrayList; |
4 | | -import java.util.LinkedList; |
5 | | - |
6 | | -import __Intro_Prime.Prime; |
| 3 | +import java.util.*; |
7 | 4 |
|
8 | 5 | // Hints: |
9 | 6 | // - Create Cell<K,V> generic class |
10 | 7 | // - Represent everything as ArrayList of LinkedLists of Cells |
11 | 8 |
|
12 | 9 | public class Hash<K, V> { |
13 | | - private int MAX_SIZE; // Initial MAX_SIZE of our ArrayList |
14 | | - private ArrayList<LinkedList<Cell<K,V>>> items; // Our main data structure to hold Cells |
15 | | - private int numItems; // number of items saved in our data structure |
16 | | - private boolean [] primes; // will hold prime numbers as "true" |
17 | | - private int primeArraySize; // size of our "boolean[] prime" array |
| 10 | + int numBuckets = 3; // small to test resizing. public for testing. |
| 11 | + private ArrayList<LinkedList<Cell<K, V>>> lists = new ArrayList<>(numBuckets); |
| 12 | + private final double LOAD_FACTOR = 0.7; |
| 13 | + private int numItems = 0; |
18 | 14 |
|
19 | | - /* Constructor */ |
20 | 15 | public Hash() { |
21 | | - MAX_SIZE = 3; // small to test resizing |
22 | | - items = new ArrayList<>(MAX_SIZE); |
23 | | - for (int i = 0; i < MAX_SIZE; i++) { |
24 | | - items.add(new LinkedList<>()); // Crucial. We don't magically get LinkedLists even though we created an ArrayList of a certain size |
25 | | - } |
26 | | - numItems = 0; |
27 | | - primeArraySize = 100; |
28 | | - primes = Prime.generatePrimes(primeArraySize); |
| 16 | + initializeLists(lists); |
29 | 17 | } |
30 | 18 |
|
31 | | - public Hash(int size) { |
32 | | - this(); |
33 | | - MAX_SIZE = size; |
| 19 | + private void initializeLists(ArrayList<LinkedList<Cell<K, V>>> lists) { |
| 20 | + for (int i = 0; i < numBuckets; i++) { |
| 21 | + lists.add(new LinkedList<>()); |
| 22 | + } |
34 | 23 | } |
35 | 24 |
|
36 | | - /* Bad hash function: may not distribute keys uniformly */ |
37 | 25 | public int hashCodeOfKey(K key) { |
38 | | - return key.toString().length() % MAX_SIZE; // should the % be done here or elsewhere? |
| 26 | + return key.hashCode() % numBuckets; |
39 | 27 | } |
40 | 28 |
|
41 | 29 | public void put(K key, V value) { |
42 | | - /* Find the LinkedList that we should put the Cell into */ |
43 | | - int hashKey = hashCodeOfKey(key); |
44 | | - LinkedList<Cell<K, V>> list = items.get(hashKey); |
| 30 | + // Find the LinkedList that we should put the Cell into. |
| 31 | + int hashCode = hashCodeOfKey(key); |
| 32 | + LinkedList<Cell<K, V>> list = lists.get(hashCode); |
45 | 33 |
|
| 34 | + // See if we should overwrite an old Cell. |
46 | 35 | for (Cell<K, V> cell : list) { |
47 | | - if (cell.equivalent(key)) { |
48 | | - cell.setValue(value); // overwrites the old value. |
| 36 | + if (cell.key == key) { |
| 37 | + cell.value = value; // overwrites the old value. |
49 | 38 | return; |
50 | 39 | } |
51 | 40 | } |
| 41 | + |
| 42 | + // Create new Cell and add it to our LinkedList. |
| 43 | + list.add(new Cell<K, V>(key, value)); |
52 | 44 | numItems++; |
53 | | - if (calculateLoadFactor() > 0.7) { |
54 | | - increaseSize(); |
| 45 | + |
| 46 | + // Rehash if our load factor is too high. |
| 47 | + double loadFactor = (double) numItems / numBuckets; |
| 48 | + if (loadFactor > LOAD_FACTOR) { |
| 49 | + rehash(); |
55 | 50 | } |
56 | | - list.add(new Cell<K, V>(key, value)); // create a new Cell and add it to our LinkedList |
57 | 51 | } |
58 | 52 |
|
59 | 53 | public V get(K key) { |
60 | | - /* Find the LinkedList that may contain the value */ |
61 | | - int code = hashCodeOfKey(key); |
62 | | - LinkedList<Cell<K, V>> list = items.get(code); |
| 54 | + // Find the LinkedList that may contain the value. |
| 55 | + int hashCode = hashCodeOfKey(key); |
| 56 | + LinkedList<Cell<K, V>> list = lists.get(hashCode); |
63 | 57 |
|
64 | | - if (list == null) { |
65 | | - return null; |
66 | | - } |
67 | 58 | for (Cell<K, V> cell : list) { |
68 | | - if (cell.equivalent(key)) { |
69 | | - return cell.getValue(); |
| 59 | + if (cell.key == key) { |
| 60 | + return cell.value; |
70 | 61 | } |
71 | 62 | } |
72 | | - |
73 | | - return null; // failure |
| 63 | + return null; // key doesn't exist. |
74 | 64 | } |
75 | 65 |
|
76 | | - /* Used to resize array when it gets too full */ |
77 | | - public double calculateLoadFactor() { |
78 | | - return (double) numItems / MAX_SIZE; |
79 | | - } |
80 | | - |
81 | | - public void increaseSize() { |
82 | | - int nextPrime = getNextPrime(); |
83 | | - if (nextPrime == -1) { // error |
84 | | - return; |
85 | | - } |
86 | | - Hash<K, V> hashNew = new Hash<K, V>(nextPrime); |
87 | | - for (LinkedList<Cell<K, V>> list : items) { |
| 66 | + private void rehash() { |
| 67 | + ArrayList<LinkedList<Cell<K, V>>> temp = lists; |
| 68 | + numBuckets *= 2; |
| 69 | + lists = new ArrayList<>(numBuckets); |
| 70 | + initializeLists(lists); |
| 71 | + numItems = 0; |
| 72 | + for (LinkedList<Cell<K, V>> list : temp) { |
88 | 73 | for (Cell<K, V> cell : list) { |
89 | | - hashNew.put(cell.getKey(), cell.getValue()); |
| 74 | + put(cell.key, cell.value); |
90 | 75 | } |
91 | 76 | } |
92 | | - MAX_SIZE = nextPrime; |
93 | | - items = hashNew.items; |
94 | | - } |
95 | | - |
96 | | - public int getNextPrime() { |
97 | | - return Prime.getNextPrime(primes, 2 * MAX_SIZE); |
98 | | - } |
99 | | - |
100 | | - public int getMaxSize() { |
101 | | - return MAX_SIZE; |
102 | 77 | } |
103 | 78 |
|
104 | 79 | @Override |
105 | 80 | public String toString() { |
106 | 81 | StringBuffer result = new StringBuffer(); |
107 | | - for (LinkedList<Cell<K, V>> list : items) { |
| 82 | + for (LinkedList<Cell<K, V>> list : lists) { |
108 | 83 | for (Cell<K, V> cell : list) { |
109 | | - result.append("key = " + cell.getKey() + " value = " + cell.getValue() + "\n"); |
| 84 | + result.append("key = " + cell.key + " value = " + cell.value + "\n"); |
110 | 85 | } |
111 | 86 | } |
112 | 87 | return result.toString(); |
|
0 commit comments