HashMap学习笔记

it2023-09-01  72

1、HashMap底层数据结构

底层数据结构: 数组+链表+红黑树(JDK1.8以后)

1.1 数组

数组: 使用一段连续的存储单元存储数据,对于指定下标的查找,时间复杂度为O(1),对于一般的插入删除操作,涉及到数组元素的移动,其平均复杂度为O(n)。所以说虽然数组读取速度快,但是前提是需要知道数组对应元素的下标索引。

1.2 链表

链表: 对链表的新增,删除操作在查找到操作位置后,只需要处理节点间的引用即可,时间复杂度为O(1),查找操作需要遍历链表中的所有节点逐一进行比对,时间复杂度为O(n)。

1.3 红黑树

红黑树: 一种接近平衡二叉搜索树,在每个节点上增加一个存储为表示节点的颜色,可以是Red或Black,通过对任何一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的, 支持查找、插入、删除等操作,其平均时间复杂度最坏为O(logn<n) 红黑树的5个特性: 1. 每个节点要么是红色,要么是黑色。 2. 根结点事黑色。 3. 每个叶结点(叶结点即指树尾端null指针或null结点)都是黑色的。 4. 如果一个结点是红色,那么它的两个子结点都是黑色。 5. 对于任意结点而言,其到叶结点树尾端null指针的每条路径都包含相同数量的黑结点。

2、HashMap底层存储原理

2.1 HashMap的key为空时

HashMap中的key允许为空,当key为空的时候会单独开辟一个空间用于存储当前的值。具体实现 // JDK1.8之后HashMap中的寻址算法,其算法规则效果等同于 hash % length但是性能要更好,另外也是由于这个原因所以HashMap初始化的时候数组长度需要为2的n次方,例如HashMap创建时默认的数组长度为16。 hash & (length - 1) // JDK1.8之后HashMap中的Hash算法的优化(h = key.hashCode()) ^ (h >>> 16) static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }

2.2 HashMap取值原理

HashMap的取值原理:由于HashMap底层是由数组 + 链表 + 红黑树组成,因此根据key取值的时候并不仅仅只比较key在HashMap底层数组对应的下标,还会比较下标对应链表/红黑树每个对象中的hashCode值,只有链表/红黑树中的对象的hashCode值与对应key的hashCode相等才能确定当前key对应的对象。底层源码实现: 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<K,V>)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); } }

2.3 HashMap链表与红黑树的转换

HashMap底层由链表转成红黑树的阈值为:8,之所以不在刚开始就使用红黑树而使用链表,是因为链表的插入性能高于红黑树。具体底层源码实现 static final int TREEIFY_THRESHOLD = 8; 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; }

3、模拟HashMap自定义Map类

Map接口

package com.xununan.www.map; /** * @packageName com.xununan.www.map * @className Map * @description: 自定义Map * @author: xu * @create: 2020/04/26 22:25 */ public interface Map<K, V> { public V put(K k, V v); public V get(K k); public int size(); public interface Entry<K, V> { public K getKey(); public V getValue(); } }

Map实现:数组 + 链表的方式

package com.xununan.www.map; /** * @packageName com.xununan.www.map * @className MyMap * @description: 自定义Map类 * @author: xu * @create: 2020/04/26 22:27 */ public class MyMap<K, V> implements Map<K, V> { private Entry[] table = null; private int size = 0; public MyMap() { table = new Entry[16]; } /** * 功能描述:put <br> * 1. key查询hashCode * 2. hashCode取模获得下标 * 3. 根据下标找到数组对象 * 4. 是否为空进行存储 * @param k * @param v * @return V * * @Author xu * @Date 2020-04-26 22:32 * @Version 1.0.0 */ @Override public V put(K k, V v) { int index = this.hash(k); Entry<K, V> entry = table[index]; if (entry == null) { table[index] = new Entry(k, v, k.hashCode(), null); size ++ ; } else { table[index] = new Entry(k, v, k.hashCode(), entry); } return (V) table[index].getValue(); } private int hash(K k) { return Math.abs(k == null ? 0 : k.hashCode() % 16); } /** * 功能描述:get <br> * 1. key找到index * 2. 数组位置 * 3. 判断当前对象是否有值,有的话比较对象的hashcode * 4. 如果当前hashcode值详单则直接返回如果不相等则继续比较下一个对象的hashcode * @param k * @return V * * @Author xu * @Date 2020-04-26 22:43 * @Version 1.0.0 */ @Override public V get(K k) { int index = this.hash(k); Entry<K,V> entry = table[index]; if (entry == null) { return null; } do { if (k.hashCode() == entry.hashCode) { return entry.getValue(); } else { entry = entry.next; } } while (entry != null); return null; // return this.find(index, entry); } private V find(int index, Entry<K,V> entry) { if (entry.hashCode == index) { return entry.getValue(); } else { if (entry.next != null) { return find(index, entry.next); } else { return null; } } } @Override public int size() { return size; } class Entry<K, V> implements Map.Entry<K, V> { K k; V v; int hashCode; Entry<K, V> next; public Entry(K k, V v, int hashCode, Entry<K, V> next) { this.k = k; this.v = v; this.hashCode = hashCode; this.next = next; } @Override public K getKey() { return k; } @Override public V getValue() { return v; } } }

以上为个人总结的一些笔记,若有雷同纯属巧合,若有错误欢迎指出,谢谢!

最新回复(0)