一、概述
HashMap最早出现在JDK1.2中,是基于Map接口的实现,储存的内容是键值对(key-value),HashMap中的键和值都可以为null。HashMap的实现并不是同步的,也就是说它不是线程安全的。
二、数据结构
在JDK1.8中,HashMap的数据结构为数组+链表+红黑树
,红黑树是HashMap在JDK1.8新引入的数据结构,主要是为了解决链表过长导致查询效率变成O(n)的问题。
当插入一个键值对时,HashMap会根据key计算出hash,再根据hash确定在数组中的位置,如果发生hash碰撞,将使用链表的形式储存,当链表过长时,将链表转为红黑树。
三、属性
默认容量
/** * The default initial capacity - MUST be a power of two. */ static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
最大容量
/** * The maximum capacity, used if a higher value is implicitly specified * by either of the constructors with arguments. * MUST be a power of two <= 1<<30. */ static final int MAXIMUM_CAPACITY = 1 << 30;
默认负载因子
/** * The load factor used when none specified in constructor. */ static final float DEFAULT_LOAD_FACTOR = 0.75f;
将链表转为红黑树的阈值
/** * The bin count threshold for using a tree rather than list for a * bin. Bins are converted to trees when adding an element to a * bin with at least this many nodes. The value must be greater * than 2 and should be at least 8 to mesh with assumptions in * tree removal about conversion back to plain bins upon * shrinkage. */ static final int TREEIFY_THRESHOLD = 8;
将红黑树转为链表的阈值
/** * The bin count threshold for untreeifying a (split) bin during a * resize operation. Should be less than TREEIFY_THRESHOLD, and at * most 6 to mesh with shrinkage detection under removal. */ static final int UNTREEIFY_THRESHOLD = 6;
将链表转为红黑树的最小容量
/** * The smallest table capacity for which bins may be treeified. * (Otherwise the table is resized if too many nodes in a bin.) * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts * between resizing and treeification thresholds. */ static final int MIN_TREEIFY_CAPACITY = 64;
储存元素的数组
/** * The table, initialized on first use, and resized as * necessary. When allocated, length is always a power of two. * (We also tolerate length zero in some operations to allow * bootstrapping mechanics that are currently not needed.) */ transient Node<K,V>[] table;
将数据转化为Set的形式储存,主要用于迭代
/** * Holds cached entrySet(). Note that AbstractMap fields are used * for keySet() and values(). */ transient Set<Map.Entry<K,V>> entrySet;
数组中元素的个数
/** * The number of key-value mappings contained in this map. */ transient int size;
修改次数
/** * The number of times this HashMap has been structurally modified * Structural modifications are those that change the number of mappings in * the HashMap or otherwise modify its internal structure (e.g., * rehash). This field is used to make iterators on Collection-views of * the HashMap fail-fast. (See ConcurrentModificationException). */ transient int modCount;
扩容阈值
/** * The next size value at which to resize (capacity * load factor). * * @serial */ // (The javadoc description is true upon serialization. // Additionally, if the table array has not been allocated, this // field holds the initial array capacity, or zero signifying // DEFAULT_INITIAL_CAPACITY.) int threshold;
负载因子
/** * The load factor for the hash table. * * @serial */ final float loadFactor;
5.3 get
get方法首先调用hash方法,计算出key的哈希值,在通过getNode方法得到对应的元素,如果不等于null,返回元素的value,否则返回null。
public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; }
getNode方法实现:
final Node<K,V> getNode(int hash, Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n; K k; // 数组已经初始化,并且传入的hash值对应的数组下标元素不为null if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { // key等于头节点的key,返回头节点 if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k)))) return first; // key不等于头节点的key if ((e = first.next) != null) { // 红黑树情况 if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); // 链表情况,遍历链表,直到找到相同的key或者遍历到结尾 do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }
5.4 remove
remove方法同样先调用hash方法计算出key的哈希值,再调用removeNode方法移除元素。
public V remove(Object key) { Node<K,V> e; return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value; }
removeNode方法:
final Node<K,V> removeNode(int hash, Object key, Object value, boolean matchValue, boolean movable) { Node<K,V>[] tab; Node<K,V> p; int n, index; // 数组已经初始化,并且传入的hash值对应的数组下标元素不为null if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1) & hash]) != null) { Node<K,V> node = null, e; K k; V v; // 传入的key等于头节点的key,待删除的节点为头节点 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) node = p; else if ((e = p.next) != null) { // 红黑树情况,调用TreeNode.getTreeNode方法获得要删除的节点 if (p instanceof TreeNode) node = ((TreeNode<K,V>)p).getTreeNode(hash, key); // 链表情况,遍历得到要删除的节点 else { do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { node = e; break; } p = e; } while ((e = e.next) != null); } } // 如果node不等于null,说明之前匹配到了要删除的节点 // 如果matchValue等于false,则不需要匹配该节点的value,否则需要node的value和传入的value相等才能删除 if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) { // 如果该节点是红黑树节点,调用TreeNode.removeTreeNode方法移除节点 if (node instanceof TreeNode) ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable); // 如果该节点是头节点,只需要把数组对应的下标指向下一个节点 else if (node == p) tab[index] = node.next; // 如果不是头节点,此时的p为前一个节点,只需要将前一个节点的next指向当前节点的next else p.next = node.next; // 修改次数+1 ++modCount; // 元素个数-1 --size; afterNodeRemoval(node); return node; } } return null; }