您的位置:首页 > 职场人生

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

2020-08-31 19:11 441 查看

HashMap源码分析系列 – HashMap的继承体系,内部类,成员变量

从今天起,争取一周之内整理完HashMap有关的各种问题。网上有很多HashMap的源码分析的帖子,我看了一些。当然也有写的非常好的,不过大多的源码分析帖企图一篇文章就把问题讲完,有时候看起来就很混乱。而且贴了较多的源码。这里我准备分为6个部分,分别上传五篇篇博文,希望用更清晰的方式,来总结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 {

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

增删改相关

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

查询相关

返回值 方法名 描述
int
hashCode()
返回此地图的哈希码值。
int
size()
返回此地图中键值映射的数量。
Collection<V>
values()
返回此地图中包含的值的Collection视图。
boolean
isEmpty()
如果此地图不包含键值映射,则返回
true
V
get(Object key)
返回到指定键所映射的值,或
null
如果此映射包含该键的映射。
boolean
containsKey(Object key)
如果此映射包含指定键的映射,则返回
true
boolean
containsValue(Object value)
如果此地图将一个或多个键映射到指定的值,则返回
true

比较相关

返回值 方法名 描述
boolean
equals(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的继承体系,以及结构,进行讲解。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: