HashMap原理

本文详细介绍了HashMap在JDK1.8之前的实现,包括数组+链表的数据结构以及拉链法解决哈希冲突。在JDK1.8之后,HashMap引入了红黑树,当链表长度超过8时,会转换为红黑树以优化查找性能。同时,文章还涵盖了HashMap的构造方法和put操作的实现细节。
摘要由CSDN通过智能技术生成

目录

HashMap 简介

底层数据结构分析

JDK1.8 之前

JDK1.8 

 构造方法

put 方法


HashMap 简介

        HashMap 主要用来存放键值对,基于哈希表的 Map 接口实现,是常用的 Java 集合之 一,是非线程安全的。

        JDK1.8 之前 HashMap 由 数组+链表 组成的,数组是 HashMap 的主体,链表则是主要 为了解决哈希冲突而存在的(“拉链法”解决冲突)。

        JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑 树)时,将链表转化为红黑树,以减少搜索时间。 HashMap 默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍。并且, HashMap 总是使用 2 的幂作为哈希表的大小。

底层数据结构分析

JDK1.8 之前

        JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用,也就是 链表散列。 HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同覆盖,不同拉链法解决冲突。

        所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法为了 防止一些实现比较差的 hashCode() 方法 ,即可以减少碰撞。 

//jdk8
static final int hash(Object key) {
    int h;
    // key.hashCode():返回散列值也就是hashcode
    // ^ :按位异或
    // >>>:无符号右移,忽略符号位,空位都以0补齐
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

//jdk7

static int hash(int h) {
    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}

        “拉链法” :将链表和数组相结合。创建一个链表数组,数组中每一格 就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中。

JDK1.8 

        当链表长度大于阈值(默认为 8)时,会首先调用 treeifyBin() 方法。这个方法会根据 HashMap 数组来决定是否转换为红黑树。只有当数组长度大于或者等于 64 的情况下, 才会执行转换红黑树操作,以减少搜索时间。否则,就是只是执行 resize() 方法对数 组扩容

 构造方法

// 默认构造函数。
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

// 包含另一个“Map”的构造函数
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);//下面会分析到这个方法
}

// 指定“容量大小”的构造函数
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

// 指定“容量大小”和“加载因子”的构造函数
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " + in
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " + loadFac
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}

put 方法

        HashMap 只提供了 put 用于添加元素,putVal 方法只是给 put 方法调用的一个方法,并 没有提供给用户使用。 对 putVal 方法添加元素的分析如下:

1. 如果定位到的数组位置没有元素 就直接插入。

2. 如果定位到的数组位置有元素就和要插入的 key 比较,如果 key 相同就直接覆盖, 如果 key 不相同,就判断 p 是否是一个树节点,如果是就调用 e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value) 将元素添加进入。如 果不是就遍历链表插入(插入的是链表尾部)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值