面试回答之Hashmap

本文深入探讨HashMap的存储原理,从JDK1.8的Entry和Node结构,到涉及的数据结构如数组、链表、红黑树。详细阐述了哈希算法、负载因子、路由寻址公式及HashMap的重要方法,包括put、resize等,揭示HashMap何时扩容的机制。
摘要由CSDN通过智能技术生成

1、hashmap存储原理

jdk1.8 对 hashmap 底层的实现进行了优化,例如引入了红黑树的数据结构和扩容的优化。

HashMap的底层是:数组+红黑树+链表
在这里插入图片描述
在 JDK1.7 中HashMap 的数据是存储在 类型为 Entry 的一个 table 数组 中的,在JDK1.8 中使用的是 Node,Node 是 Map.Entry 的一个子类。

1)Entry

Entry 是 HashMap 的一个静态内部类:Entry 中封装了 key 和 value
在这里插入图片描述
在这里插入图片描述
key 和 Value 是已知的,next 指的是指向下一个链表结点,那 hash 是什么呢?
Node.hash 是指 key 对应的 hash 值。

2)Node

Node是Entry的一个子类:
在这里插入图片描述
Node 类中的这个 hash,是 key 的 hash 值经过一次扰动后得到的 哈希值。

Hash 会发生碰撞,有相同 key 的会放置到链表中。Hash 碰撞带来的问题:相同 key 的链表的长度会很长,但是 get(key) 请求,在找节点时,时间复杂度会很大。

2、HashMap涉及到的数据结构

散列就是来整合数组和链表的优势和劣势的,在散列表的数组中保存的数据是链表。

1)数组

数组的缺点:不能扩展空间
数组的优点:根据index查找的快

2)链表

链表的缺点:只保留head节点,但是想要访问最后一个元素,只能从头元素开始遍历
链表的优点:链表可以扩展空间

3)平衡二叉树

它是一棵空树,或者它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

4)红黑树

红黑树的提出,是为了解决相同 key 的链表的长度会很长,从而带来的链化问题,提高查找效率。

一种含有红黑结点,并且能自平衡的二叉查找树。

它具备的性质:
1)每个结点要么是黑色,要么是红色;
2)根节点是黑色;
3)每个叶子结点是黑色;
4)每个红色结点的两个子结点一定都是黑色,红色结点不能和红色结点直接相连;
5)任意一个结点到叶子结点的路径都包含数量相同的黑结点。

3、hash算法

哈希算法是一类算法的统称,也叫作散列算法。

f(data)=key;
  • 输出数据长度固定:输入任意长度的 data 数据,经过哈希算法处理后输出一个定长的数据 key,该输出就是散列值(把任意长度的输入,通过Hash算法变成固定长度的输出)。
  • 单向特征:无法由 key 逆推出 data,即哈希算法是一种单向密码机制,即它是一个从明文到密文的不可逆映射,只有加密过程而没有解密过程。
  • 常用的哈希算法:MD4、MD5、SHA-1

4、负载因子

比如我们存储70个元素,但我们可能为这70个元素申请了100个元素的空间,70/100=0.7,这个数字称为负载因子。

5、路由寻址公式

(table.length - 1) & node.hash()

假设 n=table.length
首先要明白 tab[(n-1) & hash] 中的 (n-1) & hash 即 路由寻址公式 的妙处:
(1)保证不会发生数组越界。数组的长度的二进制形式是 1000…000,那么 n-1 的二进制形式就是 0111…111,这个值和hash值进行与操作,结果一定不会比数组的长度值大,即不会发生数组越界。
(2)保证元素尽可能地均匀分布。假设此时

n=16;   //0x10000
n-1=15;   //0x1111

假设现在有两个元素要插入,一个哈希值是8,二进制为 1000; 一个哈希值是9,二进制为1001。

见 7 中的 hash() 函数,

假如table.length-1为15即0x1111,那么使参与“&”运算的不止是hashCode的低16位,使其也包含高位的值(hashCode的高16位 异或 hashCode的低16位,然后进行与运算)。

6、HashMap中较为重要的几个常量

1static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 ,即1左移4位,缺省table大小
2static final int MAXIMUM_CAPACITY = 1 << 30;  //table最大长度,即数组最大长度
3static final float DEFAULT_LOAD_FACTOR = 0.75f;  //缺省负载因子大小,0.75是科学家精确计算后的
4static final int TREEIFY_THRESHOLD = 8;  //树化阈值,当发生哈希碰撞后会形成链表,链表长度到8后,可能形成一个树了
5static final int UNTREEIFY_THRESHOLD = 6
HashMap是一种散列表,它存储的内容是键值对(key-value)映射。当我们将一个键值对存储在HashMap中时,它会根据键的哈希码找到对应的桶(bucket),然后将键值对存储在桶中。当我们需要获取一个键对应的值时,HashMap会根据键的哈希码找到对应的桶,然后在桶中查找对应的值。 在面试中,如果被问到HashMap是否有序,我们需要回答HashMap不是有序的,因为HashMap中的键值对是根据哈希码存储的,而哈希码是无序的。如果被问到HashMap的存储原理,我们需要回答HashMap是通过哈希算法将键映射到桶中的,然后将键值对存储在桶中。如果被问到哈希算法的底层实现,我们需要回答说哈希算法的底层实现是哈希表。 以下是一个简单的HashMap的实现代码,仅供参考: ```java public class MyHashMap<K, V> { private static final int DEFAULT_CAPACITY = 16; private static final float DEFAULT_LOAD_FACTOR = 0.75f; private Entry<K, V>[] table; private int size; private int capacity; private float loadFactor; public MyHashMap() { this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR); } public MyHashMap(int capacity, float loadFactor) { this.capacity = capacity; this.loadFactor = loadFactor; this.table = new Entry[capacity]; } public void put(K key, V value) { int index = hash(key); Entry<K, V> entry = table[index]; while (entry != null) { if (entry.key.equals(key)) { entry.value = value; return; } entry = entry.next; } Entry<K, V> newEntry = new Entry<>(key, value); newEntry.next = table[index]; table[index] = newEntry; size++; if (size > capacity * loadFactor) { resize(); } } public V get(K key) { int index = hash(key); Entry<K, V> entry = table[index]; while (entry != null) { if (entry.key.equals(key)) { return entry.value; } entry = entry.next; } return null; } private int hash(K key) { return key.hashCode() % capacity; } private void resize() { capacity *= 2; Entry<K, V>[] newTable = new Entry[capacity]; for (Entry<K, V> entry : table) { while (entry != null) { Entry<K, V> next = entry.next; int index = hash(entry.key); entry.next = newTable[index]; newTable[index] = entry; entry = next; } } table = newTable; } private static class Entry<K, V> { K key; V value; Entry<K, V> next; public Entry(K key, V value) { this.key = key; this.value = value; } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值