HashMap源码分析(JDK1.8)

一、介绍

  1. 基本概念

    HashMap其实就是数据结构的哈希表,是一个为了快速查找定义的数据结构,旨在提高在一个数组中查找任意一个元素的时间复杂度。存储数据时给定(Key,Value),通过hash()函数得到hash作为表数组(table)的下标,查询时直接通过table[hash(key)]即可查到value,大幅提高查询效率而且这与数组元素的是否有序无关

  2. 哈希冲突(哈希碰撞)

    理想情况下是通过改造hash函数可以一次查询到想要的value,然而这必定是会有两个不同的key求出的hash值是一样的情况,那么table[hash(key)]就有两个元素,这是不允许的,这种情况称为哈希冲突

  3. 解决哈希冲突

    解决哈希冲突的方法有很多种,而HashMap使用的是链地址法(拉链法)【事实上JDK1.8使用的是链表和红黑树相互转化】,将产生冲突的两个value用链表连接起来,而他们的hash值是一样的,table数组每一个下标对应着一条链表,一条链表称为一个桶(bucket),一个桶中有一个或多个结点

在这里插入图片描述

  1. 装载因子(HashMap中称为负载因子)

    负载因子满足 s i z e ( 已 有 元 素 的 总 容 量 ) c a p a c i t y ( t a b l e 数 组 长 度 ) = l o a d f a c t o r y ( 负 载 因 子 ) \frac{size(已有元素的总容量)}{capacity(table数组长度)}=loadfactory(负载因子) capacity(table)size()=loadfactory()可以看出

    负载因子越大元素在table数组中更为密集,这就导致计算hash值时更容易产生哈希冲突;

    负载因子越小,数组需要的空间就越大造成空间浪费,这是一种空间换时间的解决方案

二、源码流程分析

友情提示:注释为个人理解,原英文注释可自行翻看源码,有大段解释文字在代码注释上哦,手机访问时点击代码可横屏显示,观看更舒适(若发现错误可私聊或评论矫正)。
1、首先认识这个类的相关变量

    // 哈希表,即上文说的table数组,容量必须为2的指数次方,后面会介绍原因
    transient Node<K,V>[] table;

    /**
     * Holds cached entrySet(). Note that AbstractMap fields are used
     * for keySet() and values().
     */
    transient Set<Map.Entry<K,V>> entrySet;

    // 所有的(key,value)对总数量大小
    transient int size;

    // 要调整大小的下一个大小值(容量*负载系数),size大于它则进行扩容
    int threshold;

    // 负载因子
    final float loadFactor;

    // 默认初始化容量为16,容量必须为2的指数次方
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    // 由于容量为2的指数次方,故最大容量为2的30次方(由于int二进制为32位且最高位为符号位)
    static final int MAXIMUM_CAPACITY = 1 << 30;

    // 默认负载因子大小为0.75,f表示为浮点数,这个大小是经过计算和实际检验的合适值
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    // 树化阈值,当一个桶中的结点数 > 8时将会把桶的链表形式转化为红黑树
    static final int TREEIFY_THRESHOLD = 8;

    // 解除树化的阈值,当一个桶中的结点数 < 6时会把桶的红黑树形式转化为链表
    static final int UNTREEIFY_THRESHOLD = 6;

    // 最小树化容量,当容量<这个值,即使达到了TREEIFY_THRESHOLD也不转化为红黑树而是进行扩容
    static final int MIN_TREEIFY_CAPACITY = 64;

静态内部类Node结点分析

	static class Node<K,V> implements Map.Entry<K,V> {
   
        // 计算的hash值
        final int hash;
        // 分别是设置的key和value值
        final K key;
        V value;
        // 指向下一个结点
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
   
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final K getKey()        {
    return key; }
        public final V getValue()      {
    return value; }
        public final String toString() {
    return key + "=" + value; }

        // 重写hashcode方法使node结点返回地址利用到key和value两个地址
        public final int hashCode() {
   
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        public final V setValue(V newValue) {
   
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        // 重写equals方法,如果对象o的引用等于该node返回true
        // 如果o是Map的结点类型且key和value都相等也返回true
        public final boolean equals(Object o) {
   
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
   
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值