HashMap、LinkedHashMap、TreeMap

2024-04-01 03:44

本文主要是介绍HashMap、LinkedHashMap、TreeMap,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、HashMap

  • 底层接口和原理HashMap实现了Map接口,并且其底层是基于哈希表(散列表/数组)的数据结构。它使用键(Key)对象的哈希码来确定键值对(Entry)存储的位置。在Java 1.8及以后,当链表长度大于一定阈值(默认是8)时,链表会被转换为红黑树来提高搜索效率。

  • 性质

    • 查询速度:理想情况下,HashMap可以提供接近O(1)的时间复杂度进行查找、插入和删除操作。但如果出现大量哈希冲突,性能可能会降低至O(n)(在Java 1.8中,由于链表转为红黑树,其性能会提升到O(log n))。
    • 顺序HashMap不保证元素的顺序,当进行插入和删除操作时,元素的顺序也可能发生变化。HashMap是无序的。
    • 并发性HashMap本身不是线程安全的,如果需要多线程并发访问,可以使用Collections.synchronizedMap方法来包装HashMap或者使用ConcurrentHashMap

2、LinkedHashMap

LinkedHashMap继承自HashMap并且实现了Map接口。其内部维护了一个双向链表,在哈希表的基础上保持了元素插入的顺序。因此,迭代访问LinkedHashMap时,元素将按照插入的顺序(或者如果指定了access-order则按照最近最少使用顺序)返回。

每个LinkedHashMap的条目(entry)除了包含哈希表中所必需的元素(键、值和哈希值)外,还包含两个指针,分别指向链表中前一个和后一个条目。这二者组成了一个双向链表。

在构造LinkedHashMap时,可以指定一个特殊的顺序模式,指定为access-order。默认情况下,LinkedHashMap是按照插入的顺序来排序条目,但如果启用了access-order模式,那么LinkedHashMap将会按照访问顺序排序,即最近访问的条目会被放到链表的尾端。

LinkedHashMap 的特性

  • 保序:不像HashMapLinkedHashMap会保持元素的插入顺序。
  • 性能LinkedHashMap在性能上类似于HashMap,因为它使用了相同的桶结构。对于插入、删除和查找操作,其时间性能依赖于哈希函数和初始容量等因素。
  • 内存开销:由于其内部维护了额外的双向链表来记录顺序,LinkedHashMapHashMap有稍微高一点的内存开销。

使用场景

  • 在Map中保持插入顺序:适用于需要保持顺序的缓存场景,比如记录文件中的键值对顺序。
  • LRU缓存(Least Recently Used, 最近最少使用):通过将LinkedHashMap配置为按访问顺序排序,你可以建立一个简单的LRU缓存。
  • 当需要通过插入顺序或者访问顺序迭代Map时: LinkedHashMap可以比其他Map实现更高效地进行迭代,因为迭代的时间复杂度是O(n),与元素的数量成正比。

3、TreeMap

  • 底层接口和原理TreeMap实现了SortedMap接口,它基于红黑树的数据结构。红黑树是一种自平衡的二叉搜索树,它可以保证在最坏情况下基本操作如搜索、插入、删除的时间复杂度都保持在O(log n)。

  • 性质

    • 查询速度TreeMap中的操作时间复杂度是O(log n),由于是基于红黑树,即使在最坏的情况下也能够保持较好的性能。
    • 顺序TreeMap保证元素以键的自然顺序或者自定义的Comparator进行排序。因此,它是有序的,可以非常方便地从中得到一个区间的子集。
    • 并发性TreeMap也不是线程安全的,同样需要外部同步机制来保障并发访问的安全性。

4、扩容

HashMapLinkedHashMap内部的数组(称为table)达到特定的装载因子(load factor)时,容量扩容流程(称为rehashing)被触发。默认装载因子的值为0.75,即当数组中75%的桶被使用时,就会进行扩容。这个装载因子是一个在创建HashMap时可以设置的参数,用于平衡时间和空间的使用效率。

以下是HashMap扩容流程的大致步骤:

  1. 创建新数组:根据当前数组容量的两倍创建一个新的数组。例如,如果当前数组容量为16,新数组的容量将是32。

  2. 重新计算索引:使用每个键(Key)的哈希值重新计算在新数组中的新索引。因为数组大小改变了,根据哈希值计算得出的索引可能也会改变。

  3. 重新分配元素:遍历旧数组,将每个元素移动到新数组的相应位置。如果冲突发生(即多个键哈希到新数组的同一位置),将保持链表或红黑树的结构不变,即如果旧数组使用链表存储碰撞的元素,则将这些元素作为链表重新链接;如果使用红黑树,则作为红黑树处理。

  4. 更新引用:一旦所有元素都被移动到新数组,将HashMap内部对数组的引用更新为新数组的引用。

在Java 8中,扩容时每个桶位置的元素会分割到两个位置。由于新数组的大小是原来的两倍,一个关键的观察结果是,元素的新位置要么是原位置,要么是原位置加上旧容量的大小。这是因为在Java中,哈希表中的位置是通过哈希值与数组长度减一进行位与操作计算得出的。

以下是在Java 8及以后版本中HashMap扩容时重新分配元素的高效特性:

  1. 不变的元素:对于没有冲突的桶,元素位置保持不变。
  2. 链表拆分:对于链表存储的桶,每个链表会被拆分成两个链表,这一操作可以在O(n)时间内完成。
  3. 红黑树拆分:对于红黑树存储的桶,树被拆分为两棵新树,并保持其平衡性。

这个过程中,不需要再次计算哈希值,因为哈希值存储在节点中,可以直接决定元素移动到原位置还是新位置(原位置加旧容量的大小)。

请注意,扩容(rehashing)是一项计算成本较高的操作,因为需要重新计算数组中每个元素的位置,所以在使用HashMap时应当尽量预估合适的初始容量以避免频繁扩容。

这篇关于HashMap、LinkedHashMap、TreeMap的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/866130

相关文章

HashMap中常用的函数

假设如下HashMap<String, Integer> map = new HashMap<>();获取value值1、返回key为a的valueget(a)2、返回key为a的value,若没有该key返回0getOrDefault(a,0)新增键值对1、新增键值对(a,1)put(a,1)2、如果key为a的键不存在,则存入键值对(a,1)putIfAbsent(a,1)3

hashmap的存值,各种遍历方法

package com.jefflee;import java.util.HashMap;import java.util.Iterator;import java.util.Map;public class HashmapTest {// 遍历Hashmap的四种方法public static void main(String[] args) {//hashmap可以存一个null,把

Java重修笔记 第四十八天 TreeSet 类、TreeMap 类

TreeSet 类 1. TreeSet 底层是 TreeMap 2. 使用默认构造器创建的 TreeSet 对象,添加顺序和取出顺序不是有序的 3. 如果添加的是字符串或数字,它们默认会按照字母顺序或数值顺序进行排序 4. 可以在构造器中传入一个 Comparator 比较器来手动制定比较规则,之后传入的数据会根据改规则自动进行比较排序,如果根据比较器比较出的结果是相同的,即 com

容器第五课,Map和HashMap的基本用法,hashMap和HashTable的区别

Map接口 实现Map接口的类用来存储键(Key) --- 值(value)对。 Map接口的实现类有HashMap和TreeMap类 Map类中存储的键---值对通过键来标识,所以键值不能重复 package com.pkushutong.Collection;import java.util.HashMap;import java.util.Map;/*** 测试Map的基本用法

面试:HashMap

文章引用: http://www.importnew.com/7099.html https://blog.csdn.net/niuwei22007/article/details/52005329 HashMap的工作原理是近年来常见的Java面试题。几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道Hashtable和HashMap之间的区别,那么

HashMap 源码分析(删除+总结)JDK1.8

文章来源: 1 https://segmentfault.com/a/1190000012926722#articleHeader7 2 https://www.zhihu.com/question/20733617 3 https://tech.meituan.com/java-hashmap.html 4 https://www.zhihu.com/question/5752

【HashMap】深入原理解析

【HashMap】深入原理解析 分类: 数据结构 自考 equals与“==”(可以参考自己的另一篇博文)   1,基本数据类型(byte,short,char,int,long,float,double,boolean) 使用“==” 对比的是具体的值是否相等 2,复合数据类型 “== ”对比的是内存中存放的地址 object中的equals初始行为是比较内存中的地址,但在

剖析LRU算法及LinkedHashMap源码实现机制

一、简述 LRU(Least Recently Used),注意L代表的不是Latest,翻译成中文通常叫:近期最少使用算法、最近最少使用算法。LRU与LFU(Least Frequently Used)、FIFO(First In First Out)是3种常用的缓存算法(页面置换算法)。缓存算法的应用场景有很多,例如操作系统在物理内存不足时触发的磁盘交换、CPU中L1、L2、L3缓存淘汰

HashMap初始化指定容量后,遇到不够用时,还会扩容吗?

先说答案:会 public class temp{public static void main(String agrs[]){HashMap hashMap = new HashMap(20);System.out.println(hashMap.size);//输出0, 要记得 容量是容量,尺寸是尺寸for(int i=0;i,30; i++){hashMap.put(i,i);System

Java集合框架(Set与Map,HashSet与HashMap,TreeSet与TreeMap)

这是一个介绍集合类,数组以及容器关系的截图,便于我们对集合的理解。 一.Set和Map Set代表一种集合元素无序、不可重复的集合,Map则代表一种由多个key-value(键-值)对组成的集合。 从表面上看,它们之间的相似性很少,但实际上Map和Set之间有莫大的联系。可以说,Map集合是Set集合的扩展。 如果只考察Map集合的Key,不难发现,这些Map集合的key具有一个特