HashMap源码解析
1.hashmap
1.1hashmap介绍
实现map接口,一种重要的数据结构,采用key-value键值对的形式来保存,可以都为null,但key值不能相同,继承map类,实现map接口
JDK1.7采用数组加链表,JDK1.8采用数组+链表+红黑树
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
2.hashmap实现
2.1创建一个hashmap用例
View Code
View Code
View Code
View Code
View Code
HashMap<String,Double> map=new HashMap<>();
当我们debug时,java首先进入Hashmap类,进行方法构建
static final float DEFAULT_LOAD_FACTOR = 0.75f /* * 当新建一个hsahmap首先进的方法,提交负载因子 * */ public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted }
loadFactor:事先设定好的负载因子,一般选0.75,当数组扩容时的最低判断
DEFAULT_LOAD_FACTOR:事先设定值
注意:
1 public HashMap(int initialCapacity) { 2 this(initialCapacity, DEFAULT_LOAD_FACTOR); 3 } 4 public HashMap(int initialCapacity, float loadFactor) { 5 if (initialCapacity < 0) 6 throw new IllegalArgumentException("Illegal initial capacity: " + 7 initialCapacity); 8 if (initialCapacity > MAXIMUM_CAPACITY) 9 initialCapacity = MAXIMUM_CAPACITY; 10 if (loadFactor <= 0 || Float.isNaN(loadFactor)) 11 throw new IllegalArgumentException("Illegal load factor: " + 12 loadFactor); 13 this.loadFactor = loadFactor; 14 this.threshold = tableSizeFor(initialCapacity); 15 }
hashmap中几个同名的构造方法,用来指定初始化的数组长度及构造因子大小,能够用来做最小的数组扩容判断
2.2实现put()方法
当我们往hashmap中放入值时,例如:
1 map.put("k1",0.2); 2 map.put("g4",0.4); 3 map.put("f2",0.1);
调用源码中的方法:
1 public V put(K key, V value) { 2 return putVal(hash(key), key, value, false, true); 3 }
先计算key的hash值
1 static final int hash(Object key) { 2 int h; 3 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); 4 }
当key不为null时,计算hash
1 (h = key.hashCode()) ^ (h >>> 16):
1. h >>> 16 是什么,有什么用?
取出高16位,
1 0000 0100 1011 0011 1101 1111 1110 0001 >>> 16 0000 0000 0000 0000 0000 0100 1011 0011
2. 为什么 h = key.hashCode()) 与 (h >>> 16) 异或
讲到这里还要看一个方法indexFor,在jdk1.7中有indexFor(int h, int length)方法。jdk1.8里没有,但原理没变。下面看下1.7源码
1 static int indexFor(int h, int length) { 2 return h & (length-1); 3 }
1.8中用tab[(n - 1) & hash]代替但原理一样。
这个方法返回值就是数组下标。我们平时用map大多数情况下map里面的数据不是很多。这里与(length-1)相&,
hasCode()转换
1 public int hashCode() { 2 int h = hash; 3 if (h == 0 && value.length > 0) { 4 char val[] = value; 5 // 6 for (int i = 0; i < value.length; i++) { 7 h = 31 * h + val[i]; 8 } 9 hash = h; 10 } 11 return h; 12 }
putval()方法
1 /** 2 * Implements Map.put and related methods 3 * 4 * @param hash hash for key 5 * @param key the key 6 * @param value the value to put 7 * @param onlyIfAbsent if true, don't change existing value 8 * @param evict if false, the table is in creation mode. 9 * @return previous value, or null if none 10 */ 11 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, 12 boolean evict) { 13 Node<K,V>[] tab; Node<K,V> p; int n, i; 14 //table就是一个Entry容器,用来存放键值对 15 /* 16 *当entry为空时,会初始化序列进入resize()方法,用分配原始的数组值来 17 * 18 * 19 **** 20 */ 21 // 如果存储元素的table为空,则进行必要字段的初始化 22 if ((tab = table) == null || (n = tab.length) == 0) 23 n = (tab = resize()).length; 24 // 如果根据hash值获取的结点为空,则新建一个结点 25 if ((p = tab[i = (n - 1) & hash]) == null) 26 tab[i] = newNode(hash, key, value, null); // 此处 & 代替了 % (除法散列法进行散列) 27 // 这里的p结点是根据hash值算出来对应在数组中的元素 28 else { 29 Node<K,V> e; K k; 30 // 如果新插入的结点和table中p结点的hash值,key值相同的话 31 if (p.hash == hash && 32 ((k = p.key) == key || (key != null && key.equals(k)))) 33 e = p; 34 // 如果是红黑树结点的话,进行红黑树插入 35 else if (p instanceof TreeNode) 36 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); 37 else { 38 for (int binCount = 0; ; ++binCount) { 39 // 代表这个单链表只有一个头部结点,则直接新建一个结点即可 40 if ((e = p.next) == null) { 41 p.next = newNode(hash, key, value, null); 42 // 链表长度大于8时,将链表转红黑树 43 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st 44 treeifyBin(tab, hash); 45 break; 46 } 47 if (e.hash == hash && 48 ((k = e.key) == key || (key != null && key.equals(k)))) 49 break; 50 // 及时更新p 51 p = e; 52 } 53 } 54 // 如果存在这个映射就覆盖 55 if (e != null) { // existing mapping for key 56 V oldValue = e.value; 57 // 判断是否允许覆盖,并且value是否为空 58 if (!onlyIfAbsent || oldValue == null) 59 e.value = value; 60 afterNodeAccess(e); // 回调以允许LinkedHashMap后置操作 61 return oldValue; 62 } 63 } 64 ++modCount; // 更改操作次数 65 if (++size > threshold) // 大于临界值 66 // 将数组大小设置为原来的2倍,并将原先的数组中的元素放到新数组中 67 // 因为有链表,红黑树之类,因此还要调整他们 68 resize(); 69 // 回调以允许LinkedHashMap后置操作 70 afterNodeInsertion(evict); 71 return null; 72 }

浙公网安备 33010602011771号