Java中的HashMap源码分析

本文详细分析了Java中的HashMap,从其继承体系、Map概念、哈希表原理到存储结构。HashMap采用数组+链表+红黑树的方式解决哈希冲突,当链表长度达到8时转为红黑树。此外,文章还介绍了HashMap的构造方法、put和get操作、扩容机制、散列冲突解决方案及快速失败机制。

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

目录

HashMap的继承体系

什么是Map

哈希表

存储结构

 HashMap的属性

Node内部类

HashMap的构造方法

数组容量的计算

put( K key,V value)方法

HashMap的扩容机制

HashMap新的数组的填充

哈希值的获取方式和table下表的计算

HashMap的get操作

HashMap 中的红黑树

散列冲突解决方式

快速失败(fail—fast)

总结


HashMap的继承体系

1.HashMap实现了Serializable接口,可以被序列化

2.HashMap实现了Cloneable接口,可以被克隆

3.HashMap继承自AbstractMap类,实现了Map接口

什么是Map

1.Map是一个接口,他是key-value的键值对,一个map不能包含重复的key,并且每一个key只能映射一个value;

2.Map接口提供了三个集合视图:key的集合,value的集合,key-value的集合;

3.Map内元素的顺序取决于Iterator的具体实现逻辑,获取集合内的元素实际上是获取一个迭代器,实现对其中元素的遍历;

4.Map接口的具体实现中存在三种Map结构,其中HashMap和TreeMap都允许存在null值,而HashTable的key不允许为空,但是HashMap不能保证遍历元素的顺序,TreeMap能够保证遍历元素的顺序。
 

哈希表

 哈希表(HashTable,散列表)是根据key-value进行访问的数据结构,他是通过把key映射到表中的一个位置来访问记录,加快查找的速度,其中映射的函数叫做散列函数,存放记录的数组叫做散列表,哈希表的主干是数组

 上面的图中就是一个值插入哈希表中的过程,那么存在的问题就是不同的值在经过hash函数之后可能会映射到相同的位置上,当插入一个元素时,发现该位置已经被占用,这时候就会产生冲突,也就是所谓的哈希冲突,因此哈希函数的设计就至关重要,一个好的哈希函数希望尽可能的保证计算方法简单,但是元素能够均匀的分布在数组中,但是数组是一块连续的且是固定长度的内存空间,不管一个哈希函数设计的多好,都无法避免得到的地址不会发生冲突,因此就需要对哈希冲突进行解决。

    (1)开放定址法:当插入一个元素时,发生冲突,继续检查散列表的其他项,直到找到一个位置来放置这个元素,至于检查的顺序可以自定义;

    (2)再散列法:使用多个hash函数,如果一个发生冲突,使用下一个hash函数,直到找到一个位置,这种方法增加了计算的时间;

    (3)链地址法:在数组的位置使用链表,将同一个hashCode的元素放在链表中,HashMap就是使用的这种方法,数组+链表的结构。
 

存储结构

HashMap采用了(数组 + 链表 + 红黑树)的实现结构,数组的一个元素称作为桶。

数组中的每一个元素值为链表,此链表为单向链表,jdk1.8之后使用的是红黑树的存储体系

在添加元素时,会根据key的hash值计算出元素在数组中的位置,如果该位置中没有元素,则直接把元素存储在此位置,如果该位置有元素了,则把元素以链表的形式存放在链表的尾部
 

 HashMap的属性

 //默认的初始容量为16
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    //最大的容量为2的30次方
    static final int MAXIMUM_CAPACITY = 1 << 30;
 
    //默认的装载因子
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    //当桶中元素的个数大于8时进行树化
    static final int TREEIFY_THRESHOLD = 8;
    
    //当桶中的元素个数小于等于6时转换为链表
    static final int UNTREEIFY_THRESHOLD = 6;
   
    //桶的个数达到64个时进行树化
    static final int MIN_TREEIFY_CAPACITY = 64;

    //数组,元素称为桶
    transient Node<K,V>[] table;
   
    transient Set<Map.Entry<K,V>> entrySet;
 
    transient int size;

Node内部类

static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;//存储key的hash值
        final K key;
        V value;
        Node<K,V> next;
        ......
}
  1. 数组容量:
    数组的容量默认为16个,最大为2的30次方

  2. 装载因子:
    装载因子用来计算容量达到多少时进行扩容,默认为0.75

  3. 树化:
    当容量达到64个且链表的长度达到8个时才进行树化,当链表小于6个时才进行反树化

HashMap的构造方法

//1.指定容量和负载因子
public HashMap(int initialCapacity, float loadFactor) {
    //判断初始容量是否合法
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                            initialCapacity);
    //判断初始容量是否大于最大容量,如果大于则初始化为最大容量 1 << 30
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAP
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值