Java--HashMap底层浅析

本文介绍了Java中的HashMap数据结构,包括HashMap与Hashtable的区别,HashMap在JDK7和JDK8的实现方式,以及为什么在JDK8中选择红黑树。还提及了LinkedHashMap用于有序遍历的特性,以及HashMap的扩容策略和负载因子。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Map
Map
接口
Hashtable
接口
HashMap
实现类
SortedMap
有序接口
Properties
实现类
LinkedHashMap
实现子类类
TreeMap
实现类
Map及其实现类
  • Hashtable:

    • 在jdk1.0时出现,为在java最早出现的键值对,之所以在jdk1.2之后出现HashMap是因为Hashtable保证了线程的安全效率,但是效率低,而HashMap不保证线程的安全,其效率高于Hashtable

    • 在Hashtable中大部分方法都添加了Synchronized的关键词,从而保证了线程的安全性

    • HashMap可以存储null值的key和value

    • 而在之后的即使涉及到线程安全的问题时,也不再使用Hashtable,而是使用Collections的工具类使得HashMap变为线程安全的

  • HashMap:

    • JDK1.2时出现

    • 当下JDK最常用的Map实现类

    • 不能保证线程的安全,但是配合Collections的工具方法可以实现线程的安全

    • 实现数据结构:

      ​ JDK7.0之前:

      ​ - 数组 + 链表

      ​ JDK8.0:

      ​ - 数组 + 链表 + 红黑树

  • LinkedHashMap:

    • JDK1.4时出现

    • 遍历Map时可以按照添加的顺序进行遍历,而不是随机的Key值进行遍历,原因:在基本的HashMap的基础上,添加了前后指针,使得可以指向添加前后的元素,从而完成有序遍历的操

    • 对于频繁的遍历操作,LinkedHashMap的效率高于HashMap

  • TreeMap:

    • 底层实现与普通的HashMap不同,对于一般有排序特点的数据结构,底层使用的为红黑树
    • 按照key进行排序便于有序的遍历
    • 通过实现Comparable或定制排序Comparator从而完成排序
  • Properties:

    • 常用来处理配置文件
    • Key和Value均为String类型
  • CurrentHashMap:

    • 实现线程安全
    • 实现分段锁
    • 保证了高并发
Entry
  • 在Map中Put的并非两个独立属性,而是一个Entry的接口匿名实现,Entry的Key实现和Value实现
  • key为不可重复的,无序的(Set作为容器【并非HashSet,但是可强转为HashSet】)
  • value为可重复的无序的,使用Collection进行存储
  • key所在的类需要重写hashCodeequals方法
HashMap底层实现
jdk7.0

在这里插入图片描述

  • 当进行HashMap map = new HashMap()的初始化后,创建了长度位16的Entry数组

  • 对于put(key,value):

    1. 调用key所在类的hashCode,若hashCode与已插入的值不相同,则添加成功,否则:

    2. 调用key所在类的equals,若与已存在的的Equals的值不相同则添加成功,否则:

    3. 刷新所对应的key的value【这也是reSet value的方法】

  • 在不断添加的过程中,若涉及到扩容问题,默认扩容为原来的2倍:

    • 扩容条件,数组长度 >= 12(临界因子 * 16)且插入位置不空(若插入位置为空则直接插入即可,不需要生成链表)
  • 在索引的位置存储元素的方式为链表

  • 添加新的元素采用头插法,即:

    void createEnrty(int hash,K key,V value,int bucketIndex){
        Entry<K,V> e = table[bucketIndex];
    	table[bucketIndex] = new Entry(hash,key,value,e);
    	size++;
    }
    
    static class Entry<K,V>{
    	final K key;
    	V value;
    	Entry<K,V> next;
    	int hash;
    
    	Enrty(int h,K k,V v,Entry<K,V> n){  // 头插法添加新的entry元素到entrylist中
    		value = v;
    		next = n;
    		key = k;
    		hash = h;
    	}
    
    }
    

在这里插入图片描述

  • 为什么要到threshold就扩容,而不是满了再扩容?
    • ans:因为数组为hash码,可能有的位置始终没有元素,而有的位置形成了链表,所以,当阈值因子较小时,链表的长度较短,但是利用率低,当阈值因子较长时,链表较长,查找速率下降,所以阈值因子为0.75
jdk8.0
  • 在创建Map时,底层没有创建长度为16的数组
  • jdk8.0底层为Node类型的数组,而非entry类型的数组
  • 首次调用Put方法时底层创建长度为16的数组
  • 当某一个索引的数组位置上的元素以链表形式存在的数据个数 > 8且数组的长度 > 64时,该位置的所有数据升级为红黑树,红黑树为二叉排序树,使得当hashCode相同时查找的效率更快,而不是查找链表
  • 在JDK8.0中HashMap的构造方法变为了尾插法,而不是之前的头插法
LinkedHashMap
static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after; // 在继承了HashMap的Node的基础上添加了指向前后Entry的指针
        Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值