深度解析HashMap集合底层原理

本文详细探讨了HashMap的底层原理,包括为何重写equals和HashCode方法,时间复杂度分析,以及JDK1.8的HashMap特点。讲解了HashMap的五大关键点:集合初始化、数据寻址Get、数据存储Put、节点删除以及扩容机制,并分析了多线程环境下的扩容问题。此外,还讨论了HashMap转换为红黑树的情况及其目的,以及如何避免内存溢出问题。最后,文章涵盖了HashMap的面试题,如Key为null的存储位置、有序性以及如何提高效率等。
摘要由CSDN通过智能技术生成

前置知识#

==和equals的区别#

对于基本类型变量来说,只能使用 == ,因为基本类型的变量没有方法。使用==比较是值比较

对于引用类型的变量来说,==比较的两个引用对象的地址是否相等。所有类都是继承objcet类,而object类是equals方法比较的也是对象的地址是否相等,如果类没有重写equals方法,使用 == 和equals方法效果是一样的

为什么要重写equals和HashCode#

HashCode方法:底层采用C语言编写,根据对象地址转换为整数类型

如果两个对象的HashCode相等,对象的内容至不一定相等;hash碰撞的问题

如果使用equals方法比较两个对象内容值相等的情况下,那么hashcode的值也相等

因为equals默认情况下Object类采用==比较对象,那么比较的是内存地址是否相等,当数据类型只要不是基本类型,那么比较永远不会相等。

set集合存储的就是不重复的对象,底层就是hashmap,依据equals和hashcode进行判断

时间复杂度#

时间复杂度为O(n) 从头查询到尾部,查询多次

时间复杂度为O(1) 查询一次 比如根据数组下标查询

时间复杂度为O(logn) 平方查询 比如红黑树,

效率:O(1)>O(logn)>O(n)

(不带符号右移) >>>#

无符号右移就是右移之后,无论该数为正还是为负,右移之后左边都是补上0

无符号右移运算符和右移运算符的主要区别在于负数的计算,因为无符号右移是高位补0,移多少位补多少个0

15>>>2=0000 1111 右移两位=0000 0011=3

^异或运算#

相同为0,不同为1

2^3= 0010^0011=0001=1

&(与运算)#

00得0 11得1 01得0

2&3=0010&0011=0010=2

位移操作:1<<2=4,1左移两位为什么等于4#

这里的1是十进制,而计算机交流是用二进制,所以先要将1用二进制表示出来。

每一个符号(英文、数字或符号等)都会占用1Bytes的记录,每一个中文占2Byte

而一个1Bytes占8个bit,也就是8个二进制位

8位二进制数:28种不同状态 0000 0000 ~1111 1111=0~255=28=256

1的二进制表示0000 0001,然后进行位移操作。

位移操作向左边位移,后面空出来的补上0,越往左边越大,把0000 0001向左位移2位,变成了0000 0100,二进制0000 0100转化十进制所以为4,也可以说每左移一位是乘以2

8>>2?

0000 1000右移2位0000 0010,转化为10进制等于2

10>>2?

0000 1010右移2位0000 0010,转化为10进制等于2

1<<30?

0000 0001左移30位01000000 00000000 00000000 00000000,转化为10进制等于1073741824,也就是230

HashMap集合特点及源码分析(JDK1.8)#

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

HashMap继承了AbstractMap类,实现了Cloneable克隆接口、Serializable序列化接口、Map接口

特点:数组+链表+红黑树构成

img

HashMap重要的五大点#

1.集合初始化#

HashMap成员变量#

//默认初始化hashmap容量
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //2的4次幂  16
//hashmap最大容量1073741824
static final int MAXIMUM_CAPACITY = 1 << 30;//2的30次幂
//扩容因子 16*0.75=12 达到12就会进行扩容
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//链表中存储元素的数量 > 8 时,会自动转换为红黑树
static final int TREEIFY_THRESHOLD = 8;
//删除元素时,如果一个红黑树中中存储元素数量 < 6 后,会自动转换为链表
static final int UNTREEIFY_THRESHOLD = 6;
//数组容量>64&链表长度>8 转为红黑树
static final int MIN_TREEIFY_CAPACITY = 64;

//阙值,用于判断是否扩容,threshold=容量*扩容因子=16*0.75=12
int threshold;
//扩容因子实际大小
final float loadFactor;
//HashMap中元素的数量 transient表示不能被序列化
transient int size;
//集合修改次数  防止多线程篡改数据
transient int modCount;
//存储元素的数组  单向链表
transient Node<K,V>[] table;

HashMap内部数据结构#

链表

//单向链表 实现了Entry接口  由上面的数组构成了数组加链表的结构
static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    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; }

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

    public final V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
    }
   //比较两个Node是否相等
    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;
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值