✨探索Java基础 HashMap详解✨
总述
主体
1. HashMap的基本概念
2. HashMap的工作原理
3. HashMap的常用操作
4. HashMap的优缺点
5..jdk1.8对HashMap主要做了哪些优化呢?
1. 数据结构
2. 链表插入方式
3. 扩容(rehash)机制
4. 扩容时机
5. 散列函数
总结
常见面试题
常见面试题解答
1. HashMap的底层实现原理是什么?
2. 如何解决HashMap中的哈希冲突?
3. HashMap和Hashtable的区别是什么?
4. 在什么情况下HashMap会发生扩容?
5. 为什么HashMap不是线程安全的?如何实现线程安全的HashMap?
ConCurrentHashMap Put流程:
HashMap源码
1 put方法流程
2 扩容
3 get方法
在Java中, 是一个常用的数据结构,它实现了接口,允许我们通过键值对的形式存储和快速查找数据。的底层是基于哈希表(hash table)的实现,它的高效性和灵活性使其在各种编程场景中广受欢迎。本文将详细介绍的原理、使用方法、优缺点,并提供一些常见的面试题。
1. HashMap的基本概念
JDK1.7的数据结构是 数组 + 链表
说⼀下JDK1.8的数据结构吧: JDK1.8的数据结构是 数组 + 链表 + 红⿊树 。
是一个散列表,它存储键值对(key-value pairs),每个键对应一个唯一的值。不保证顺序,并且允许值作为键或值。
2. HashMap的工作原理
使用哈希表来存储数据。键的哈希值通过方法计算,然后通过哈希函数将哈希值映射到数组的索引位置上。通过链地址法(chaining)来解决哈希冲突,即在每个数组索引处存储一个链表(Java 8及之后版本采用红黑树以提高性能)。
3. HashMap的常用操作
- 添加元素: 使用方法。
插入流程:
- 获取元素: 使用方法。
查找流程:
- 移除元素: 使用方法。
- 遍历元素:
4. HashMap的优缺点
优点:
- 快速查找: 平均时间复杂度为O(1)。
- 灵活: 可以存储不同类型的对象,允许键和值。
缺点:
- 非线程安全: 多线程情况下需要手动同步。
- 不保证顺序: 插入顺序和遍历顺序可能不同。
5..jdk1.8对HashMap主要做了哪些优化呢?
1. 数据结构
- 变化:从数组 + 链表改为数组 + 链表或红黑树。
- 原因:当发生哈希冲突时,元素会被存储在链表中。如果链表过长,则转换为红黑树,将查找时间复杂度由 O(n) 降低至 O(logn)。
2. 链表插入方式
- 变化:从头插法改为尾插法。
- 说明:在 JDK 1.7 中,新元素被放置到链表头部;而在 JDK 1.8 中,遍历链表并将新元素放置到链表尾部。
- 原因:JDK 1.7 的头插法在扩容时可能导致链表反转,在多线程环境下可能会产生环状结构,而尾插法则避免了这一问题。
3. 扩容(rehash)机制
- 变化:采用更简单的判断逻辑来决定新位置。
- 说明:JDK 1.7 在扩容时需要重新计算每个元素的新哈希值以确定其在新数组中的位置;JDK 1.8 则通过更简化的逻辑直接确定新位置,通常是原索引或者原索引加上新增容量大小。
- 原因:这种改变提高了扩容操作的效率。
4. 扩容时机
- 变化:从先判断是否需要扩容再进行插入,变更为先执行插入操作,之后再判断是否需要扩容。
- 原因:这样可以减少不必要的扩容检查次数,尤其是在频繁插入但不经常达到扩容阈值的情况下。
5. 散列函数
- 变化:减少了散列函数的操作次数。
- 说明:JDK 1.7 的散列函数执行了四次位移和异或操作,而 JDK 1.8 只执行一次。
- 原因:尽管多次操作可能提供更好的分布性,但实际上边际效益不大,简化后的散列函数提高了计算速度且对于大多数应用场景来说足够有效。
是Java中一个强大且高效的集合类,用于快速查找和存储键值对。理解其工作原理和常用操作对于提高编程效率和解决复杂问题非常有帮助。
- 的底层实现原理是什么?
- 如何解决中的哈希冲突?
- 和的区别是什么?
- 在什么情况下会发生扩容?
- 为什么不是线程安全的?如何实现线程安全的?
常见面试题解答
1. HashMap的底层实现原理是什么?
的底层是基于哈希表(hash table)实现的。它内部使用一个数组来存储元素,每个数组的元素被称为“桶”(bucket)。当我们向中插入一个键值对时,会先根据键的方法计算出哈希值,然后通过哈希函数将哈希值映射到数组的索引位置上。通过链地址法(chaining)来解决哈希冲突,即每个桶中存储一个链表(Java 8及之后版本采用红黑树以提高性能)。
2. 如何解决HashMap中的哈希冲突?
采用链地址法(chaining)来解决哈希冲突。具体方法是,每个桶中存储一个链表(或者在Java 8及之后版本中,当链表长度超过一定阈值时,会转换成红黑树),所有映射到同一索引位置的键值对都会存储在这个链表或红黑树中。
当插入一个新的键值对时,如果该键值对的哈希值映射到的索引位置已经存在其它元素,则会将新的键值对添加到该位置的链表或红黑树中。
3. HashMap和Hashtable的区别是什么?
- 线程安全性: 是线程安全的,所有方法都是同步的,而不是线程安全的,适用于单线程环境或通过外部同步来保证线程安全。
- null键和值: 允许一个键和多个值,而不允许键和值。
- 性能: 由于的方法是同步的,因此在单线程环境下性能比差。
- 遗产: 是基于较老的类实现的,而是从Java 1.2开始作为接口的实现类。
4. 在什么情况下HashMap会发生扩容?
会在容量达到阈值(默认是当前容量的0.75倍)时发生扩容。扩容时,的容量会变为原来的两倍,并重新哈希已有的键值对,重新分配到新的桶中。扩容可以避免哈希冲突,保持的高效性。
5. 为什么HashMap不是线程安全的?如何实现线程安全的HashMap?
不是线程安全的,因为它的所有方法都不是同步的。在多线程环境下,多个线程同时修改的结构可能导致数据不一致或出现死循环。
要实现线程安全的,可以通过以下方法:
- 使用方法: 这个方法返回一个线程安全的。
- 使用: 这是Java提供的线程安全的实现,适用于高并发环境。它通过分段锁机制(Segmented Locking)来提高并发性能。
ConCurrentHashMap Put流程:
具体:整个流程和HashMap⾮常类似,只不过是先定位到具体的Segment,然后通过ReentrantLock去操作⽽已,后⾯ 的流程,就和HashMap基本上是⼀样的。
1. 计算hash,定位到segment,segment如果是空就先初始化
2. 使⽤ReentrantLock加锁,如果获取锁失败则尝试⾃旋,⾃旋超过次数就阻塞获取,保证⼀定获取锁成功
3. 遍历HashEntry,就是和HashMap⼀样,数组中key和hash⼀样就直接替换,不存在就再插⼊链表,链表同 样操作
可见下图:
1 put方法流程
2 扩容
3 get方法
觉得有用的话可以点点赞 (*/ω\*),支持一下。
如果愿意的话关注一下。会对你有更多的帮助。
每天都会不定时更新哦 >人< 。
到此这篇java字符串转map集合(java 字符串转map对象)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/jjc/14792.html