HashMap大厂面试频率高,源码太长了不想看?看完这一篇就完事了!

HashMap源码分析

笔记首页

序号内容链接地址
1HashMap的继承体系,HashMap的内部类,成员变量https://blog.csdn.net/weixin_44141495/article/details/108327490
2HashMap的常见方法的实现流程https://blog.csdn.net/weixin_44141495/article/details/108329558
3HashMap的一些特定算法,常量的分析https://blog.csdn.net/weixin_44141495/article/details/108305494
4HashMap的线程安全问题(1.7和1.8)https://blog.csdn.net/weixin_44141495/article/details/108250160
5HashMap的线程安全问题解决方案https://blog.csdn.net/weixin_44141495/article/details/108420327
6Map的四种遍历方式,以及删除操作https://blog.csdn.net/weixin_44141495/article/details/108329525
7HashMap1.7和1.8的区别https://blog.csdn.net/weixin_44141495/article/details/108402128

HashMap的继承体系,内部类,成员变量

HashMap的继承体系

HashMap是AbstractMap的子类,是Map的实现类,同时实现了Cloneable接口和Serializable接口。

一般我们给HashMap一个专业的描述:HashMap是Map接口的非同步实现类。

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

image-20200831185110011

我们看一下Map接口的方法(常见方法)

增删改相关

返回值方法名描述
Vput(K key, V value)将指定的值与该映射中的指定键相关联(可选操作)。
voidputAll(Map<? extends K,? extends V> m)将指定地图的所有映射复制到此映射(可选操作)。
Vremove(Object key)如果存在(从可选的操作),从该地图中删除一个键的映射。
default booleanremove(Object key, Object value)仅当指定的密钥当前映射到指定的值时删除该条目。
default Vreplace(K key, V value)只有当目标映射到某个值时,才能替换指定键的条目。
default booleanreplace(K key, V oldValue, V newValue)仅当当前映射到指定的值时,才能替换指定键的条目。
default voidreplaceAll(BiFunction<? super K,? super V,? extends V> function)将每个条目的值替换为对该条目调用给定函数的结果,直到所有条目都被处理或该函数抛出异常。

查询相关

返回值方法名描述
inthashCode()返回此地图的哈希码值。
intsize()返回此地图中键值映射的数量。
Collection<V>values()返回此地图中包含的值的Collection视图。
booleanisEmpty()如果此地图不包含键值映射,则返回 true
Vget(Object key)返回到指定键所映射的值,或 null如果此映射包含该键的映射。
booleancontainsKey(Object key)如果此映射包含指定键的映射,则返回 true
booleancontainsValue(Object value)如果此地图将一个或多个键映射到指定的值,则返回 true

比较相关

返回值方法名描述
booleanequals(Object o)将指定的对象与此映射进行比较以获得相等性。

转换相关

返回值方法名描述
Set<K>keySet()返回此地图中包含的键的Set视图。
Set<Map.Entry<K,V>>entrySet()返回此地图中包含的映射的Set视图。

HashMap的成员属性

常量

//默认初始化Node数组容量16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

//最大的数组容量
static final int MAXIMUM_CAPACITY = 1 << 30;

//默认负载因子0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;

//由链表转红黑树的临界值
static final int TREEIFY_THRESHOLD = 8;

//由红黑树转链表的临界值
static final int UNTREEIFY_THRESHOLD = 6;

//桶转化为树形结构的最小容量
static final int MIN_TREEIFY_CAPACITY = 64;

普通变量

trainsient关键字修饰的属性,当Map序列化时,是不参与的。

//HashMap结构修改的次数,结构修改是指更改HashMap中的映射数或以其他方式修改其内部结构(例如,rehash的修改)。该字段用于在Collection-views上快速生成迭代器。
transient int modCount;  
//Node数组下一次扩容的临界值,第一次为16*0.75=12(容量*负载因子)
int threshold;
//负载因子
final float loadFactor;
//map中包含的键值对的数量
transient int size;

复杂变量

Node<K,V>是HashMap的内部类,实现Map.Entry<K,V>接口,HashMap的哈希桶数组中存放的键值对对象就是Node<K,V>。类中维护了一个next指针指向链表中的下一个元素。值得注意的是,当链表中的元素数量超过TREEIFY_THRESHOLD后会HashMap会将链表转换为红黑树,此时该下标的元素将成为TreeNode<K,V>,继承于LinkedHashMap.Entry<K,V>,而LinkedHashMap.Entry<K,V>是Node<K,V>的子类,因此HashMap的底层数组数据类型即为Node<K,V>。

//表数据,即Node键值对数组,Node是单向链表,它实现了Map.Entry接口,长度是2的幂次倍
transient Node<K,V>[] table;
//存放具体元素的集,可用于遍历map集合
transient Set<Map.Entry<K,V>> entrySet;

HashMap的构造方法

HashMap提供了四种构造方式

  • 默认构造方式
  • 自定义容量构造方式
  • 自定义容量,加载因子构造方式
  • 传入Map构造

默认构造方法,由于HashMap是懒加载,真正的数组初始化是在第一次添加元素的时候。

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

给定加载因子和初始容量

//初始化容量以及负载因子
public HashMap(int initialCapacity, float loadFactor) {
    //判断初始化数组的容量大小
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    //判断初始化的负载因子大小和是否为浮点型
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    //初始化负载因子
    this.loadFactor = loadFactor;
    this.threshold = tableSizeFor(initialCapacity);
}  

自定义初始容量

//自定义初始化容量
public HashMap(int initialCapacity) {  
    this(initialCapacity, DEFAULT_LOAD_FACTOR);  
}  

给定一个Map容器。

//把另一个Map的值映射到当前新的Map中
public HashMap(Map<? extends K, ? extends V> m) {  
    this.loadFactor = DEFAULT_LOAD_FACTOR;  
    putMapEntries(m, false);  
}  

HashMap静态内部类

Node

static class Node<K,V> implements Map.Entry<K,V> {
    // 哈希值,HashMap根据该值确定记录的位置
    final int hash;
    // node的key
    final K key;
    // node的value
    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;
    }

    // 返回 node 对应的键
    public final K getKey()        { return key; }
    // 返回 node 对应的值
    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;
    }

    //作用:判断2个Entry是否相等,必须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.equals(value, e.getValue()))
                return true;
        }
        return false;
    }
}

TreeNode

/**
  * 红黑树节点 实现类:继承自LinkedHashMap.Entry<K,V>类
  */
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {  

    // 属性 = 父节点、左子树、右子树、删除辅助节点 + 颜色
    TreeNode<K,V> parent;  
    TreeNode<K,V> left;   
    TreeNode<K,V> right;
    TreeNode<K,V> prev;   
    boolean red;   

    // 构造函数
    TreeNode(int hash, K key, V val, Node<K,V> next) {  
        super(hash, key, val, next);  
    }  

    // 返回当前节点的根节点  
    final TreeNode<K,V> root() {  
        for (TreeNode<K,V> r = this, p;;) {  
            if ((p = r.parent) == null)  
                return r;  
            r = p;  
        }  
    }
}

HashMap的底层实现

Jdk1.8 数组+链表+红黑树(也就是Node[],Node,TreeNode)

Jdk1.7 数组+链表 (Entry[],entry)

Jdk1.8

Jdk1.7

总结

本篇主要是将HashMap的继承体系,以及结构,进行讲解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值