HashMap解读

HashMap

一、HashMap的结构

首先我们有必要了解一下HashMap的结构:

在JDK1.7及之前的版本中,HashMap的结构是由数组(,这个数组的元素也称为桶(bucket))+ 单项链表

请添加图片描述

而在JDK1.8及之后的版本中,HashMap的结构则由 数组+ 单项链表/红黑树

请添加图片描述

1.1 hash表

了解hash表这个数据结构,先了解一下hash函数

hash函数

hash函数就是根据key的值计算出应该存储地址的位置,而哈希表是基于哈希函数建立的一种查找表

在HashMap的hash函数

static final int hash(Object key) {
   
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

从hash()方法中,可以看出 HashMap是接收key为null的情况:

①key为null 返回hash值为0

②key不为null 返回 key的hashcode的高16位 亦或 低16; (这种方式是为了解决 哈希冲突)

  • 这样就完了吗?不! key!=null 得到的 并非是真正的hash值

在HashMap源码中,但凡涉及到元素的方法,都要对元素的Key进行hash()操作,接下来是获取Key的真正的Hash值:

获取hash值的方式有很多:像 平方取中法折叠法数字分析法相除取模法

其中相除取模是用的最多的,HashMap也用的正是该方法

H(key)=hash(Key.hashcode())% p (p为数组的长度)

//在HashMap中 哈希取模的 操作

H(key) = (n - 1) & hash;
//只有n为2的次方 才能有 (n - 1) & hash等价于 hash % n
//这样做的好处,采用位运算,提升效率
hash冲突

不同元素获取到的hash值可能相等

一般解决hash冲突的方式:

  1. 开放地址法
  2. 链地址法 (HashMap使用的方法)
  3. 再哈希法

1.2HashMap 通过 链地址法解决Hash冲突

首先我们先了解一下HashMap元素的结构

单项链表的节点Node
static class Node<K,V> implements Map.Entry<K,V> {
   
    					//实现了Map.Entry
        final int hash;
        final K key;
        V value;
        Node<K,V> next;  //单向链表
public final int hashCode() {
   
    //节点的hashcode:Key的hashcode 与 value的hashcode 进行异或
 		return Objects.hashCode(key) ^ Objects.hashCode(value);
 }
}
红黑树的节点Node

关于红黑树:可参考(52条消息) B树和红黑树_Beau想躺平的博客-CSDN博客

static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
   
        TreeNode<K,V> parent;  // red-black tree links
        TreeNode<K,V> left;
        TreeNode<K,V> right;
        TreeNode<K,V> prev;    // needed to unlink next upon deletion
        boolean red;  
    //有一个将单项链表 树化为 红黑树的方法
    final void treeify(Node<K,V>[] tab){
   ...}
    //也有一个将红黑树 还原为 单项链表的方法
    final Node<K,V> untreeify(HashMap<K,V> map) {
   ...}
    //红黑树 增删改查的 操作
  }

二、HashMap操作

2.1 HashMap的 操作常量

public class HashMap<K,V> ... {
   
    //默认初始化数组的长度为16
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    //数组最大长度
    static final int MAXIMUM_CAPACITY = 1 << 30;
    //默认负载因子
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    //临界值
    static final int TREEIFY_THRESHOLD = 8;
    //桶由单项链表变为红黑树的条件1:数组长度的临界值,超过64,就可以变为红黑树
    static final int MIN_TREEIFY_CAPACITY = 64;
    
}

2.2HashMap的属性

transient Node<K,V>[] table;

transient Set<Map.Entry<K,V>> entrySet;

transient int size;

transient int modCount;

final float loadFactor;

2.2创建HashMap

  • 初始化过程,不会对数组容量直接确定,只能对 临界值或负载因子进行限定
    public HashMap(int initialCapacity, float loadFactor) {
   
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;  //判断超过最大值 2^30
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        
        this.threshold = tableSizeFor(initialCapacity);//临界值要变为 2的次方(便于进行哈希取模)
    }
    public HashMap(
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值