手写HashMap 数组+链表+自动扩容+相关问题

  1 package dataStructure.myHashmap;
  2 import java.util.Objects;
  3 
  4 /**
  5  *
  6  * @param <K>
  7  * @param <V>
  8  */
  9 
 10 public class Myhashmap<K, V> implements MyMap<K, V> {
 11     private static final long serialVersionUID = 362498820763181265L;
 12 
 13     //默认的bin初始化长度 -- 必须是2的幂
 14     private static final int DEFAULT_INITIAL_CAPACITY  = 1 << 4;
 15 
 16     //数组的最长长度 1 << 30
 17     private static final int MAXIMUM_CAPACITY = 1 << 30;
 18 
 19     //默认的装载因子
 20     private static final float DEFAULT_LOAD_FACTOR = 0.75f;
 21 
 22 
 23 
 24     //树化的阙值,达到这个值就进行树化
 25     private static final int TREEIFY_THRESHOLD = 8;
 26 
 27     //从红黑树变回链表的阙值
 28     private static final int UNTREEIFY_THRESHOLD = 6;
 29 /*---------------Fields----------------------------- */
 30     //map中的元素个数
 31     private transient int size;
 32     //bin
 33     Node<K, V>[] table;
 34 
 35     //map的操作数
 36     transient int modCount = 0;
 37 
 38     //loadFactor
 39     float loadFactor;
 40 
 41     //threshold
 42     int threshold;
 43 
 44 
 45 //----------------------------------------constructor------------------------------------------------
 46     public Myhashmap(){
 47         this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
 48     }
 49     public Myhashmap(int capacity, float loadFactor){
 50         if(capacity < 0){
 51             throw new IllegalArgumentException("Illegal Initial Capacity" + capacity);
 52         }
 53         if(capacity > MAXIMUM_CAPACITY){
 54             capacity = MAXIMUM_CAPACITY;
 55         }
 56         if(loadFactor <= 0 || Float.isNaN(loadFactor)){
 57             throw new IllegalArgumentException("Illegal LoadFactory" + loadFactor);
 58         }
 59         this.size = 0;
 60         this.modCount = 0;
 61         this.loadFactor = loadFactor;
 62         //初始化阙值的时候找到大于自己设置的值的最小二次幂
 63         this.threshold = (int) (capacity * loadFactor);
 64     }
 65 
 66 
 67 //-------------------------------------Node----------------------------------------------------
 68     static class Node<K, V> implements MyMap.Entry<K, V>{
 69         final int hash;
 70         final K key;
 71         V value;
 72         Node<K, V> next;
 73         public Node(int hash, K key, V value, Node<K, V> next){
 74             this.hash = hash;
 75             this.key = key;
 76             this.value = value;
 77             this.next = next;
 78         }
 79 
 80         @Override
 81         public V getValue() {
 82             return this.value;
 83         }
 84 
 85         @Override
 86         public K getKey() {
 87             return this.key;
 88         }
 89 
 90 
 91     public final String toString(){
 92             return this.key + "=" + this.value;
 93         }
 94         //
 95         public final int hashCode(){
 96             return Objects.hashCode(key) ^ Objects.hashCode(value);
 97         }
 98         public V setValue(V newValue){
 99             V oldValue = value;
100             value = newValue;
101             return oldValue;
102         }
103     }
104     //-------------------------------------OutsideMethod-------------------------------------
105 
106     @Override
107     public V put(K k, V v) {
108         return putVal(hash(k), k, v);
109     }
110 
111     @Override
112     public V get(K k) {
113         return getNode(hash(k),k).value;
114     }
115 
116     public Node<K, V> remove(K k, V v){
117         return removeNode(hash(k), k, v);
118     }
119 
120     public int size(){
121         return size;
122     }
123 
124     public Boolean isEmpty(){
125         return size == 0;
126     }
127 
128     public boolean contaisKey(K k){
129         return getNode(hash(k), k) != null;
130     }
131 
132     //------------------------method----------------------
133 
134     final Node<K, V> getNode(int hash, Object key){
135         Node<K, V>[] tab = table;
136         int n;
137         Node<K, V> p;
138         if(tab == null || (n = tab.length) == 0){
139             return null;
140         }
141         if((p = tab[indexFor(hash, n)]) == null){
142             return null;
143         }else if(p.hash == hash && (p.key == key || (key != null && key.equals(p.key)))){
144            return p;
145         } else{
146             Node<K, V> e;
147             if((e = p.next) != null){
148                 do{
149                     if(e.hash == hash && (e.key == key || (key != null && key.equals(e.key)))){
150                         return e;
151                     }
152                 }while((e = e.next) != null);
153             }
154 
155         }
156         return null;
157     }
158 
159 
160 
161     static final int hash(Object key){
162         int h;
163         return key == null ? 0 : (h = Objects.hashCode(key)) ^ (h >>> 16);
164     }
165 
166     public V putVal(int hash, K key, V value){
167         if(key == null || value == null){
168             throw new NullPointerException("key or value is null");
169         }
170         int i = 0;
171         Node<K, V>[] tab = table;
172         Node<K, V> p;
173         int n;
174         if(tab == null || (n = tab.length) == 0) {
175            n = (tab = resize()).length;
176         }
177         if((p = tab[i = (hash & (n - 1))]) == null){
178             tab[i] = new Node<>(hash, key, value, null);
179         }else{
180             Node<K, V> e; K k;
181             //判断当前p节点是不是你要插入的节点
182             if(p.hash == hash && ((k = p.key) == key || (k.equals(key) && key != null))){
183                 e = p;
184             }else{
185                 while(true){
186                     if((e = p.next) == null){
187                         p.next = new Node<>(hash, key, value, null);
188                         break;
189                     }
190                     if(e.hash == hash && (e.key == key || (key.equals(e.key) && key != null))){
191                         break;
192                     }
193                     //向下传递
194                     p = e;
195                 }
196             }
197             if(e != null){
198                 V oldVal = e.value;
199                 e.value = value;
200                 return oldVal;
201             }
202         }
203         this.table = tab;
204         ++modCount;
205         if (++size > threshold) {
206             resize();
207         }
208         return null;
209     }
210 
211     /**
212      * 扩容方法
213      * @return
214      */
215 
216     final Node<K, V>[] resize(){
217         Node<K, V>[] oldTab = table;
218         int oldCap = (oldTab == null) ? 0 : oldTab.length;
219         int oldThr = threshold;
220         int newCap, newThr = 0;
221         if(oldCap > 0){
222             if(oldCap > MAXIMUM_CAPACITY){
223                 newThr =Integer.MAX_VALUE;
224                 return oldTab;
225             } else if((newCap = oldCap >> 1) < MAXIMUM_CAPACITY && oldCap > DEFAULT_INITIAL_CAPACITY){
226                 newCap = oldCap >> 1;
227             }
228         }
229         else if(oldThr == 0){
230             newCap = DEFAULT_INITIAL_CAPACITY;
231             newThr = (int) (loadFactor * DEFAULT_INITIAL_CAPACITY);
232         }else{
233             newCap = oldThr;
234         }
235         threshold = newThr;
236         Node<K, V>[] newTab = (Node<K, V>[]) new Node[newCap];
237         newTab = transfer(oldTab, newTab);
238         this.table = newTab;
239         return newTab;
240     }
241     final Node<K, V>[] transfer(Node<K, V>[] oldTab, Node<K, V>[] newTab){
242         int oldCap = oldTab.length;
243         int newCap = newTab.length;
244         for (int i = 0; i < oldTab.length; i++) {
245             Node<K, V> e = oldTab[i];
246             if(e != null){
247                 oldTab[i] = null;
248                 //当前桶中只有一个节点
249                 if(e.next == null){
250                     newTab[e.hash & (newCap - 1)] = e;
251                 }else{
252                     //拆分链表
253                     //低位链表
254                     Node<K, V> loHead = null, loTail = null;
255                     //高位链表
256                     Node<K, V> hiHead = null, hiTail = null;
257                     while(e != null){
258                         //链表中保持原数组位置不会改变的节点
259                         if(indexFor(e.hash, newCap) == 0){
260                             if(loTail == null){
261                                 loHead = e;
262                             }else{
263                                 loTail.next = e;
264                             }
265                             loTail = e;
266                         }else{
267                             if(hiTail == null){
268                                 hiHead = e;
269                             }else{
270                                 hiTail.next = e;
271                             }
272                             hiTail = e;
273                         }
274                         if(e.next == null){
275                             break;
276                         }
277                         e = e.next;
278                     }
279                     if(loTail != null){
280                         loTail.next = null;
281                         newTab[i] = loHead;
282                     }
283                     if(hiTail != null){
284                         hiTail.next = null;
285                         newTab[i + oldCap] = hiHead;
286                     }
287                 }
288             }
289         }
290         return newTab;
291     }
292     final int indexFor(int h, int length){
293         return h & (length - 1);
294     }
295 
296     final Node<K, V> removeNode(int hash, K k, V v){
297         if(k == null || v == null){
298             throw new ArithmeticException("this key or value is null!");
299         }
300         Node<K, V>[] tab = table;
301         int n = 0;
302         int index;
303         Node<K, V> p;
304         if(tab == null || (n = tab.length) == 0){
305             return null;
306         }if((p = tab[index = indexFor(hash, n)]) == null){
307             return null;
308         }else{
309             Node<K, V> e;
310             Node<K, V> node = null;
311             K key = p.key;
312             if(p.hash == hash && (k == key) || (key.equals(k) && k != null)){
313                 node = p;
314             }else if((e = p.next) != null){
315                 do{
316                     if(e.hash == hash && (k == e.key || (k != null && k.equals(e.key)))){
317                         node = e;
318                         break;
319                     }
320                     //双指针一起移动,p是前驱节点
321                     p = e;
322                 }while((e = e.next) != null);
323             }
324             if(node != null && node.value == v){
325                 if(node == p){
326                     tab[index] = node.next;
327                 }else{
328                     p.next = node.next;
329                 }
330                 ++ modCount;
331                 --size;
332                 return node;
333             }
334         }
335         return null;
336     }
337 
338 }
MyHashMap

问题:

1.当你想要存100个值的时候,你会设置多少的默认容量?

因为默认的loadFactor是0.75、同时默认容量必须是2的整数次幂,所以124是大于100最小的2的整数次幂了,但是124 * 0.75 < 100,这样就会触发自动扩容,自动将容量扩到248,所以直接将默认容量设置成248。

2.Hashmap的默认容量为什么必须是2的整数次幂?

主要是hash算法弄的,Hashmap中的hash是

1 final int indexFor(int h, int length){
2         return h & (length - 1);
3     }

也就是hash % (length - 1)但是%运算会很慢,所以使用位运算,假设length = 16 二进制也就是 1 0000,length - 1 = 0 1111。每一位的&运算是不是都能有0 或者1两种可能,但是如果其中一位是0的话,这一位的&运算就只能是0,会浪费很多的空间,所以必须是2的整数次幂,为了更好的利用空间。

3.在jdk1.8 HashMap引入了红黑树,同时有了树化的链表长度阙值,当长度达到8的时候就进行 就进行树化(抱歉我这里面没有实现)所以为什么是8这个值呢?

(来自网上)泊松分布,每个数组位置上分布的节点数是8的概率是万分之一,所以 更多的可能是一个数组位置上分布了很多的节点,但是链表的空间复杂度是O(n)然后红黑树的空间复杂度是O(log(n))。

4.红黑树转回成链表的阙值却是6,这是为什么呢?

为了防止红黑树和链表之间的频繁转化。

猜你喜欢

转载自www.cnblogs.com/frank9571/p/12747087.html