HashMap 原理之 HashMap 初始化(基于 JDK1.8)

在这里插入图片描述

基于 JDK1.8 进行分析

首先说几个问题,然后根据问题去分析具体情况

Q1. 默认容量是多少,负载因子是多少,扩容倍数?
Q2. 底层的存储数据结构?
Q3. 如何处理 hash 冲突?
Q4. 如何计算一个 key 的 hash 值?
Q5. 数组的长度为何是 2 的幂次方?
Q6. 扩容查找过程?

开始之前,先看一下 HashMap 的几个基本属性。

/**
 * 默认的初始化容量,必须是 2 的 幂次方
 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16  二进制 1 向左移四位

/**
  * 最大容量,左移三十位,也就是 2 的 30 次方
  */
static final int MAXIMUM_CAPACITY = 1 << 30;

/**
  * 默认的负载因子
  */
static final float DEFAULT_LOAD_FACTOR = 0.75f; //不指定的话,默认为 0.75f

/**
 * Node 节点数组, 用来表示一个 key-value
 * 内部类
 */
transient Node<K,V>[] table;

/**
 * map 中包含键值对映射的数量
 */
transient int size;

节点数组的存储使用了一个内部类 Node 来存储,该 Node 对象包含了当前对象 key 的 hash 值,key 值,value,以及下一个对象的指针。源码如下

static class Node<K,V> implements Map.Entry<K,V> {
   
    final int hash; //节点 hash
    final K key; // key
    V value; // 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; }

    public final int hashCode() {
   
        return Objects.hashCode(key) ^ Objects.hashCode(value);
    }

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

    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.equals(value, e.getValue()))
                return true;
        }
        return false;
    }
}

由上面的源码可以看出,每一个节点都存储了下一个节点,是一

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
JDK1.7和JDK1.8之后,HashMap的数据结构发生了以下变化: 1. JDK1.7中底层数据结构是由数组+单链表实现,而JDK1.8及之后使用的是数组+链表/红黑树实现。这意味着在JDK1.8中,当链表的深度大于等于8时,会将链表转换为红黑树,以提高查询效率。 2. JDK1.7中的hash值是可变的,因为有rehash的操作,而JDK1.8中的hash值是final修饰的,一旦确定就不会再重新计算。 3. JDK1.7会先判断对象是否是String,如果是,则不采用String复写的hashcode方法,以解决Hash碰撞安全问题。而JDK1.8计算出来的hash值只可能是一个,所以hash值设置为final修饰。 4. 在处理null值方面,JDK1.7中将null视为一个特殊的值,单独处理,而JDK1.8中,null的hash值计算结果为0,与普通的key没有区别。 5. 扩容方式也有所不同。JDK1.7是先扩容后插入新值,而JDK1.8是先插入新值再扩容。 6. 元素插入算法也有差异。JDK1.7采用表头插入法,在扩容时会改变链表中元素原本的顺序,可能导致链表成环的问题。而JDK1.8采用尾部插入法,在扩容时会保持链表元素原本的顺序,避免链表成环的问题。 7. 初始化和扩容基数也有所不同。JDK1.7的初始size为16,扩容时newsize = oldsize*2,而JDK1.8的size始终为2的n次幂。 综上所述,JDK1.7和JDK1.8之后的HashMap在底层数据结构、hash算法、对null的处理、扩容方式、元素插入算法以及初始化和扩容基数等方面都发生了变化。\[2\]\[3\] #### 引用[.reference_title] - *1* *3* [JDK1.7和1.8的HashMap有哪些区别?](https://blog.csdn.net/weixin_39098944/article/details/108519553)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [HashMap JDK1.7和JDK1.8的区别](https://blog.csdn.net/qq_47350811/article/details/127327440)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值