数据结构之HashMap详解(一)

在之前有一篇博客介绍了HashMap,数据结构之Hash表(哈希表)
现在对之前的内容做一个补充。
说HashMap之前,看下数组:
在这里插入图片描述
数组的优点在于查找速度快,每个区域都是紧挨着的,并且有下标,可以根据下标快速找到要查找的元素。
缺点:增删麻烦,如果在第二个元素后面再添加个元素的话,那么就需要重新建一个数组,填入对应的信息。
链表:每个内存除了存放自身的元素外,还保留下个内存区域的地址,
在这里插入图片描述
优点:插入新元素方便,只需要将指针指向新元素,新元素指向原来的下一个指针即可。
缺点:查找速度慢。链表不像数组那样有下标,链表查找元素只能从head结点开始,一个一个向下找。
而散列表则是综合了两者优点于一身的结构:
在这里插入图片描述
上面是数组,数组每个区域内是链表。
说到散列表,就应当说哈希,没有哈希,散列表就无法寻址找数据,
哈希
哈希:Hash也称散列、哈希,对应的英文都是Hash,基本原理就是把任意长度的输入,通过Hash算法变成固定长度的输出,这个映射的规则就是对应的Hash算法,而原始数据映射后的二进制串就是哈希值。
Hash特点:
1、hash值不可以反向推导出原始的数据
2、输入的数据的微小变化会得到完全不同的hash值,相同的数据可以得到相同的值。
3、哈希算法的执行效率要高效
4、hash算法的冲突概率要小
hash原理是将输入空间的值映射成hash空间内,但是hash空间的大小是要小于输入空间的大小的,所以说是存在不同的输入值被映射成相同输出的情况。
底层存储结构
在这里插入图片描述
hashmap的默认长度是16。如果要改变长度,也应当是2的次方,也就是16,32这些。当每个链表的长度超过8,达到9的时候,并且所有的元素超过64个,链表就变为红黑树。
其实hashmap就是数组+链表+红黑树组成的。
put数据原理分析
map.put(“别云”,“无用”);key就是别云,而无用就是value。
接下来获取key的hash值,也就是别云的hash值。
经过hash值扰动函数,使此hash值更散列。
构造出对象;
根据路由算法,找出对象应在数组中存放的位置。
路由寻址:
(table.length-1) & node.hash;table.length就是该hashmap的长度,node.hash就是获取字符串“别云”的hash值,将两者相与,得到要存放在数组中的位置。
hash碰撞
假如刚才的map.put(“别云”,“无用”);经过路由算法,最后存放在数组的第三个位置,而map.put(“恍惚之境”,“启明星的指引”);也是存放到第三个位置。两者都是存放到一个位置,这个就是hash碰撞。只需要将原结点指向新节点即可,形成个新链表。
链化
如果多次执行put操作,其中有多个元素都是放在位置3上,如果存放的多了,位置三上的链表的长度就会很长,此时进行查询操作,时间复杂度就会由O(1)变为O(N)。这就是链化。
jdk1.8引入红黑树
jdk1.8引入红黑树就是为了解决上面的链化问题,当结点较多的时候,链表转化为红黑树提高查询效率,红黑树是一种平衡二叉查找树,关于红黑树,这两天就会写到。
HashMap扩容
假如一个haspMap大小为16,每个块有四个结点。假如扩容之后,大小为32,那么每块则只有两个结点。这就是用空间换取时间的做法。
接下来就是对HashMap源码的介绍展示

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HashMap是Java中最常用的哈希表实现之一,它基于哈希表实现了Map接口。以下是HashMap源码的详细解释: HashMap内部是由一个数组和链表组成的,数组的每个元素称为桶,每个桶存储一个链表(可能为空),链表中的每个节点都是一个键值对(key-value pair)。 以下是HashMap的主要属性: ```java transient Node<K,V>[] table; // 存储元素的数组 transient int size; // 元素大小 int threshold; // 扩容阈值 final float loadFactor; // 负载因子 ``` 其中,table是一个transient修饰的Node数组,存储HashMap中的元素;size表示HashMap中元素的个数;threshold表示HashMap的扩容阈值,即当元素个数达到这个值时就需要扩容;loadFactor是负载因子,用于决定HashMap何时需要扩容。 以下是HashMap的主要方法: 1. put(K key, V value) :将指定的键值对添加到HashMap中,如果键已经存在,则更新对应的值。 2. get(Object key):获取指定键对应的值,如果键不存在则返回null。 3. remove(Object key):从HashMap中删除指定的键值对,如果键不存在则返回null。 4. clear():从HashMap中删除所有的键值对。 5. resize():扩容HashMap,将table的大小增加一倍。 6. hash(Object key):计算键的哈希值。 7. getNode(int hash, Object key):获取指定键的节点。 8. putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict):实际执行put操作的方法,会根据指定的参数决定是否更新已有键的值、是否删除过期键等。 HashMap的put方法实现如下: ```java public V put(K key, V value) { // 计算键的哈希值 int hash = hash(key); // 计算键在table数组中的索引 int i = indexFor(hash, table.length); // 遍历桶中的链表,查找指定键 for (Node<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { // 如果键已经存在,则更新对应的值 V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } // 如果指定键不存在,则创建新的节点,并将其添加到桶的链表中 modCount++; addEntry(hash, key, value, i); return null; } ``` 在这个方法中,我们首先计算键的哈希值,然后计算键在table数组中的索引。接着,我们遍历桶中的链表,查找指定键,如果键已经存在,则更新对应的值。否则,我们创建新的节点,并将其添加到桶的链表中。 HashMap的get方法实现如下: ```java public V get(Object key) { // 计算键的哈希值 int hash = hash(key); // 计算键在table数组中的索引 int i = indexFor(hash, table.length); // 遍历桶中的链表,查找指定键 for (Node<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { // 如果找到指定键,则返回其对应的值 return e.value; } } // 如果指定键不存在,则返回null return null; } ``` 在这个方法中,我们首先计算键的哈希值,然后计算键在table数组中的索引。接着,我们遍历桶中的链表,查找指定键,如果找到指定键,则返回其对应的值。 HashMap的remove方法实现如下: ```java public V remove(Object key) { // 计算键的哈希值 int hash = hash(key); // 计算键在table数组中的索引 int i = indexFor(hash, table.length); // 遍历桶中的链表,查找指定键 Node<K,V> prev = table[i]; Node<K,V> e = prev; while (e != null) { Node<K,V> next = e.next; Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { modCount++; size--; if (prev == e) { table[i] = next; } else { prev.next = next; } e.recordRemoval(this); return e.value; } prev = e; e = next; } // 如果指定键不存在,则返回null return null; } ``` 在这个方法中,我们首先计算键的哈希值,然后计算键在table数组中的索引。接着,我们遍历桶中的链表,查找指定键,如果找到指定键,则从链表中删除节点,并返回其对应的值。否则,我们返回null。 以上就是HashMap源码的详细解释。HashMap是一个非常常用且实用的数据结构,它的实现原理也非常值得深入学习和理解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值