diff --git a/week_01/12/HashMap1-012.md b/week_01/12/HashMap1-012.md new file mode 100644 index 0000000..64f293e --- /dev/null +++ b/week_01/12/HashMap1-012.md @@ -0,0 +1,160 @@ +#### 问题 +扩容、解决hash冲突、冲突优化是如何进行的 + +如何以数组存储键值对 +数组满了怎么扩容 +hash冲突时以链表大于阈值后,链表如何树化,左旋、右旋如何实现 +链表过大时会树化,树过大时怎么处理,会扩容吗 +如何使hash更分散不易冲突呢 + +如何遍历entry + +函数式编程如何实现 + +#### 简介 +键值对形式存储,通过负载因子控制Node[]数组扩容,hash冲突时node节点以链表存在,一个node节点大于阀值会树化 + +#### 继承体系 +![image](https://raw.githubusercontent.com/sljie1988/image/master/jdkSourceStudy/HashMapInheritSystem.png?token=AKPZOCLMNKO5IMH7VYVRQSK57FSVQ) +#### 类结构说明 +static class Node implements Map.Entry +static final class TreeNode extends LinkedHashMap.Entry +final class EntrySet extends AbstractSet> +final class EntryIterator extends HashIterator implements Iterator> +static final class EntrySpliterator extends HashMapSpliterator implements Spliterator> + +#### 源码解析 +##### 属性 +static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; +static final int MAXIMUM_CAPACITY = 1 << 30; +static final float DEFAULT_LOAD_FACTOR = 0.75f; +static final int TREEIFY_THRESHOLD = 8; +static final int UNTREEIFY_THRESHOLD = 6; +static final int MIN_TREEIFY_CAPACITY = 64; + +transient Node[] table; +transient Set> entrySet; +transient int size; +transient int modCount; + + +##### 构造方法 +public HashMap(int initialCapacity, float loadFactor) +public HashMap(int initialCapacity) +public HashMap() +public HashMap(Map m) + +##### 主要方法 +final void putMapEntries(Map m, boolean evict) + +public V put(K key, V value) +final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) +final Node[] resize() +final TreeNode putTreeVal(HashMap map, Node[] tab, int h, K k, V v) +final void treeifyBin(Node[] tab, int hash) +final void treeify(Node[] tab) +static TreeNode balanceInsertion(TreeNode root, TreeNode x) +static TreeNode rotateLeft(TreeNode root, TreeNode p) +static TreeNode rotateRight(TreeNode root, TreeNode p) + +final void split(HashMap map, Node[] tab, int index, int bit) + +##### 其他方法 +public V get(Object key) +final Node getNode(int hash, Object key) + +public V remove(Object key) +final Node removeNode(int hash, Object key, Object value, boolean matchValue, boolean movable) +final void removeTreeNode(HashMap map, Node[] tab, boolean movable) +final Node untreeify(HashMap map) +static TreeNode balanceDeletion(TreeNode root, TreeNode x) + +public boolean containsKey(Object key) +public boolean containsValue(Object value) +public void clear() + +public V compute(K key, BiFunction remappingFunction) +public V merge(K key, V value, BiFunction remappingFunction) +public void forEach(BiConsumer action) +public void replaceAll(BiFunction function) + +public Object clone() +private void writeObject(java.io.ObjectOutputStream s) +private void readObject(java.io.ObjectInputStream s) + +----------- +static class Node implements Map.Entry +final int hash; +final K key; +V value; +Node next; + +----------- +static final class TreeNode extends LinkedHashMap.Entry +TreeNode parent; // red-black tree links +TreeNode left; +TreeNode right; +TreeNode prev; // needed to unlink next upon deletion +boolean red; + +static void moveRootToFront(Node[] tab, TreeNode root) +final TreeNode getTreeNode(int h, Object k) +final void treeify(Node[] tab) +final Node untreeify(HashMap map) +final TreeNode putTreeVal(HashMap map, Node[] tab, int h, K k, V v) +final void removeTreeNode(HashMap map, Node[] tab, boolean movable) +final void split(HashMap map, Node[] tab, int index, int bit) + +static TreeNode rotateLeft(TreeNode root, TreeNode p) +static TreeNode rotateRight(TreeNode root, TreeNode p) +static TreeNode balanceInsertion(TreeNode root, TreeNode x) +static TreeNode balanceDeletion(TreeNode root, TreeNode x) + +static boolean checkInvariants(TreeNode t) + +----------- +final class EntrySet extends AbstractSet> +final class KeySet extends AbstractSet +final class Values extends AbstractCollection + +public Set> entrySet() +public Set keySet() +public Collection values() + +public final boolean contains(Object o) +public final boolean remove(Object o) +public final void forEach(Consumer> action) +public final Spliterator> spliterator() + +----------- +abstract class HashIterator +final class EntryIterator extends HashIterator implements Iterator> +final class KeyIterator extends HashIterator implements Iterator +final class ValueIterator extends HashIterator implements Iterator + +public final Iterator> iterator() +public final boolean hasNext() +public final Map.Entry next() +final Node nextNode() +public final void remove() + +----------- +static class HashMapSpliterator +static final class EntrySpliterator extends HashMapSpliterator implements Spliterator> +static final class KeySpliterator extends HashMapSpliterator implements Spliterator> +static final class ValueSpliterator extends HashMapSpliterator implements Spliterator> + +final int getFence() +public final long estimateSize() + +public EntrySpliterator trySplit() +public void forEachRemaining(Consumer> action) +public boolean tryAdvance(Consumer> action) +public int characteristics() + +#### 总结 +hashMap存储过程,将数据存入节点为key value的节点桶组中,hash冲突时,以链表方式存储,当链表长度大于8且桶组长度大于64时, +进行树化,以查找二叉树存储,通过左右旋与红黑树实现树平衡。 +hash值分散,对象地址的hash值与自身高位16位异或实现。 +扩容,以原容量2倍方式扩容,老桶组给新桶组复制时,桶组直接复制,hash冲突的链表和树化后的数据会根据情况切割成两小份复制。 + diff --git a/week_01/12/HashMap2-012.md b/week_01/12/HashMap2-012.md new file mode 100644 index 0000000..e5b79e2 --- /dev/null +++ b/week_01/12/HashMap2-012.md @@ -0,0 +1,457 @@ +#### 问题 +扩容、解决hash冲突、冲突优化是如何进行的 + +如何以数组存储键值对 +数组满了怎么扩容 +hash冲突时以链表大于阈值后,链表如何树化,左旋、右旋如何实现 +链表过大时会树化,树过大时怎么处理,会扩容吗 +如何使hash更分散不易冲突呢 + +如何遍历entry + +函数式编程如何实现 + +``` +public HashMap(int initialCapacity, float loadFactor) { + if (initialCapacity < 0) + throw new IllegalArgumentException("Illegal initial capacity: " + + initialCapacity); + if (initialCapacity > MAXIMUM_CAPACITY) + initialCapacity = MAXIMUM_CAPACITY; + if (loadFactor <= 0 || Float.isNaN(loadFactor)) + throw new IllegalArgumentException("Illegal load factor: " + + loadFactor); + this.loadFactor = loadFactor; + this.threshold = tableSizeFor(initialCapacity); +} + +// 扩容门槛为传入初始容量最近的2的n次方 +static final int tableSizeFor(int cap) { + int n = cap - 1; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; +} +``` + +``` +final void putMapEntries(Map m, boolean evict) { + int s = m.size(); + if (s > 0) { + // 桶组为空,桶组扩容门槛阈值threshold为大于参数map size最小的2次方值 + if (table == null) { // pre-size + float ft = ((float)s / loadFactor) + 1.0F; + int t = ((ft < (float)MAXIMUM_CAPACITY) ? + (int)ft : MAXIMUM_CAPACITY); + if (t > threshold) + threshold = tableSizeFor(t); + } + // map size大于桶组扩容门槛阈值threshold,扩容 + else if (s > threshold) + resize(); + for (Map.Entry e : m.entrySet()) { + K key = e.getKey(); + V value = e.getValue(); + putVal(hash(key), key, value, false, evict); + } + } +} +``` + +``` +// object hashCode值采用native方法根据对象地址来计算 +// hash值为hashCode与16位高位异或求得,使hash更分散 +static final int hash(Object key) { + int h; + return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); +} +``` + +``` +public V get(Object key) { + Node e; + return (e = getNode(hash(key), key)) == null ? null : e.value; +} + +final Node getNode(int hash, Object key) { + Node[] tab; Node first, e; int n; K k; + if ((tab = table) != null && (n = tab.length) > 0 && + (first = tab[(n - 1) & hash]) != null) { + if (first.hash == hash && // always check first node + ((k = first.key) == key || (key != null && key.equals(k)))) + return first; + if ((e = first.next) != null) { + // 树中查 + if (first instanceof TreeNode) + return ((TreeNode)first).getTreeNode(hash, key); + // 链表中查 + do { + if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + return e; + } while ((e = e.next) != null); + } + } + return null; +} +``` + +``` +public V put(K key, V value) { + return putVal(hash(key), key, value, false, true); +} + +final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { + Node[] tab; Node p; int n, i; + // 桶组为空,创建Node[] + if ((tab = table) == null || (n = tab.length) == 0) + n = (tab = resize()).length; + // 桶组存值 + if ((p = tab[i = (n - 1) & hash]) == null) + tab[i] = newNode(hash, key, value, null); + else { + Node e; K k; + // 桶组有值 + if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) + e = p; + // 桶中treeNode存值,有值则返回 + else if (p instanceof TreeNode) + e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); + else { + // 桶中linkedNode存值,有值则返回 + for (int binCount = 0; ; ++binCount) { + if ((e = p.next) == null) { + p.next = newNode(hash, key, value, null); + if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st + // 大于阈值树化 + treeifyBin(tab, hash); + break; + } + if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + break; + p = e; + } + } + // 有值修改 + if (e != null) { // existing mapping for key + V oldValue = e.value; + if (!onlyIfAbsent || oldValue == null) + e.value = value; + afterNodeAccess(e); + return oldValue; + } + } + ++modCount; + if (++size > threshold) + resize(); + afterNodeInsertion(evict); + return null; +} + +``` + +``` +final void treeifyBin(Node[] tab, int hash) { + int n, index; Node e; + // 桶组长度小于64,扩容 + if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) + resize(); + else if ((e = tab[index = (n - 1) & hash]) != null) { + TreeNode hd = null, tl = null; + do { + // 桶中linkedCode转为treeNodes + TreeNode p = replacementTreeNode(e, null); + if (tl == null) + hd = p; + else { + p.prev = tl; + tl.next = p; + } + tl = p; + } while ((e = e.next) != null); + // 树化 + if ((tab[index] = hd) != null) + hd.treeify(tab); + } +} + +final void treeify(Node[] tab) { + TreeNode root = null; + for (TreeNode x = this, next; x != null; x = next) { + next = (TreeNode)x.next; + x.left = x.right = null; + // 定义根节点 + if (root == null) { + x.parent = null; + x.red = false; + root = x; + } + else { + K k = x.key; + int h = x.hash; + Class kc = null; + for (TreeNode p = root;;) { + int dir, ph; + K pk = p.key; + // 根节点值大于传入值 + if ((ph = p.hash) > h) + dir = -1; + else if (ph < h) + dir = 1; + else if ((kc == null && + (kc = comparableClassFor(k)) == null) || + (dir = compareComparables(kc, k, pk)) == 0) + dir = tieBreakOrder(k, pk); + + TreeNode xp = p; + if ((p = (dir <= 0) ? p.left : p.right) == null) { + x.parent = xp; + // 根节点值大于传入值,存入左节点 + if (dir <= 0) + xp.left = x; + else + xp.right = x; + root = balanceInsertion(root, x); + break; + } + } + } + } + moveRootToFront(tab, root); +} +``` + +待续 +``` +static TreeNode balanceInsertion(TreeNode root, + TreeNode x) { + x.red = true; + for (TreeNode xp, xpp, xppl, xppr;;) { + if ((xp = x.parent) == null) { + x.red = false; + return x; + } + else if (!xp.red || (xpp = xp.parent) == null) + return root; + if (xp == (xppl = xpp.left)) { + if ((xppr = xpp.right) != null && xppr.red) { + xppr.red = false; + xp.red = false; + xpp.red = true; + x = xpp; + } + else { + if (x == xp.right) { + root = rotateLeft(root, x = xp); + xpp = (xp = x.parent) == null ? null : xp.parent; + } + if (xp != null) { + xp.red = false; + if (xpp != null) { + xpp.red = true; + root = rotateRight(root, xpp); + } + } + } + } + else { + if (xppl != null && xppl.red) { + xppl.red = false; + xp.red = false; + xpp.red = true; + x = xpp; + } + else { + if (x == xp.left) { + root = rotateRight(root, x = xp); + xpp = (xp = x.parent) == null ? null : xp.parent; + } + if (xp != null) { + xp.red = false; + if (xpp != null) { + xpp.red = true; + root = rotateLeft(root, xpp); + } + } + } + } + } +} + +static TreeNode rotateLeft(TreeNode root, TreeNode p) { + TreeNode r, pp, rl; + if (p != null && (r = p.right) != null) { + if ((rl = p.right = r.left) != null) + rl.parent = p; + if ((pp = r.parent = p.parent) == null) + (root = r).red = false; + else if (pp.left == p) + pp.left = r; + else + pp.right = r; + r.left = p; + p.parent = r; + } + return root; +} + +static TreeNode rotateRight(TreeNode root, TreeNode p) { + TreeNode l, pp, lr; + if (p != null && (l = p.left) != null) { + if ((lr = p.left = l.right) != null) + lr.parent = p; + if ((pp = l.parent = p.parent) == null) + (root = l).red = false; + else if (pp.right == p) + pp.right = l; + else + pp.left = l; + l.right = p; + p.parent = l; + } + return root; +} +``` + +``` +final Node[] resize() { + // threshold:扩容门槛阈值 + Node[] oldTab = table; + int oldCap = (oldTab == null) ? 0 : oldTab.length; + int oldThr = threshold; + int newCap, newThr = 0; + if (oldCap > 0) { + // 定义最大threshold + if (oldCap >= MAXIMUM_CAPACITY) { + threshold = Integer.MAX_VALUE; + return oldTab; + } + // 容量扩容两倍,threshold扩容两倍 + else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && + oldCap >= DEFAULT_INITIAL_CAPACITY) + newThr = oldThr << 1; // double threshold + } + // 定义空hashMap时 + else if (oldThr > 0) // initial capacity was placed in threshold + newCap = oldThr; + else { // zero initial threshold signifies using defaults + newCap = DEFAULT_INITIAL_CAPACITY; + newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); + } + if (newThr == 0) { + float ft = (float)newCap * loadFactor; + newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? + (int)ft : Integer.MAX_VALUE); + } + // 定义新阈值、新容量,创建新桶组 + threshold = newThr; + @SuppressWarnings({"rawtypes","unchecked"}) + Node[] newTab = (Node[])new Node[newCap]; + table = newTab; + // 旧桶组给新桶组赋值 + if (oldTab != null) { + for (int j = 0; j < oldCap; ++j) { + Node e; + if ((e = oldTab[j]) != null) { + // 清空,等待GC + oldTab[j] = null; + // 桶组赋值 + if (e.next == null) + newTab[e.hash & (newCap - 1)] = e; + // 桶组treeNode赋值 + else if (e instanceof TreeNode) + // todo 树打散成两颗树插入到新桶中 + ((TreeNode)e).split(this, newTab, j, oldCap); + // 桶组linkedNode赋值 + else { // preserve order + // todo 分化成两个链表插入到新的桶中 + Node loHead = null, loTail = null; + Node hiHead = null, hiTail = null; + Node next; + do { + next = e.next; + if ((e.hash & oldCap) == 0) { + if (loTail == null) + loHead = e; + else + loTail.next = e; + loTail = e; + } + else { + if (hiTail == null) + hiHead = e; + else + hiTail.next = e; + hiTail = e; + } + } while ((e = next) != null); + if (loTail != null) { + loTail.next = null; + newTab[j] = loHead; + } + if (hiTail != null) { + hiTail.next = null; + newTab[j + oldCap] = hiHead; + } + } + } + } + } + return newTab; +} +``` + +待续 +``` +final void split(HashMap map, Node[] tab, int index, int bit) { + TreeNode b = this; + // Relink into lo and hi lists, preserving order + TreeNode loHead = null, loTail = null; + TreeNode hiHead = null, hiTail = null; + int lc = 0, hc = 0; + for (TreeNode e = b, next; e != null; e = next) { + next = (TreeNode)e.next; + e.next = null; + if ((e.hash & bit) == 0) { + if ((e.prev = loTail) == null) + loHead = e; + else + loTail.next = e; + loTail = e; + ++lc; + } + else { + if ((e.prev = hiTail) == null) + hiHead = e; + else + hiTail.next = e; + hiTail = e; + ++hc; + } + } + + if (loHead != null) { + if (lc <= UNTREEIFY_THRESHOLD) + tab[index] = loHead.untreeify(map); + else { + tab[index] = loHead; + if (hiHead != null) // (else is already treeified) + loHead.treeify(tab); + } + } + if (hiHead != null) { + if (hc <= UNTREEIFY_THRESHOLD) + tab[index + bit] = hiHead.untreeify(map); + else { + tab[index + bit] = hiHead; + if (loHead != null) + hiHead.treeify(tab); + } + } +} +``` +