设为首页 加入收藏

TOP

2、HashMap源码分析(一)
2023-07-25 21:43:27 】 浏览:95
Tags:HashMap

特别:下文的“容量”、“数组长度”,“capacity” 都是指底层数组长度,即 table.length

1 一般数据结构及特点

  1. 数组:占用连续内存的数据结构,查找容易[O(1)],插入困难[O(n)]
  2. 链表:由一组指向(单向或者双向)的节点连接的数据结构,内存不连续,查找困难,但插入删除容易
  3. 哈希表:插入删除查找都容易的数据结构
  4. 数组下标是通过:(Node<K, V>[] 的容量-1)&(hash(key))的出来的

本章要解决的问题:

  • HashMap的数据结构实现方式
  • HashMap是怎么做到为get、put操作提供稳定的时间复杂度的
  • HashMap什么时候从单节点转成链表又是什么时候从链表转成红黑树
  • HashMap初始化时为什么要给自定义的初始容量。
  • HashMap如何保证容量始终是2的幂
  • HashMap为何要保证容量始终是2的幂
  • HashMap的hash值如何计算
  • HashMap为什么是线程不安全的

2 HashMap基本属性说明

常量部分:

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 默认初始容量 16
static final int MAXIMUM_CAPACITY = 1 << 30; //最大容量
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;  //链表转转红黑树的数组最小容量
transient int size; //HashMap的元素个数
  • default_initial_capacity:初始容量=16
  • maximum_capacity:最大容量=1<<30。
  • default_load_factor:负载因子=0.75。
  • threshold:下一个触发扩容操作的阈值,threshold = capacity * load_factor。当元素数量(size值)超过阈值时触发扩容,新容量是旧容量2倍
  • treeify_threshold:链表转红黑树时链表长度阈值=8
  • untreeify_threshold: 红黑树转链表阈值=6,红黑树节点小于6就会转成链表
  • Node<K, V> implements Map.Entry<K, V> :HashMap存放数据的基本单位,里面存有hash值、key、value、next。
  • Node<K, V>[] table:存放Node节点的数组,HashMap底层数组,数组元素可以为单节点Node、多节点链表、多节点红黑树。
  • size:成员变量,表示当前Map的键值对数量,在put、remove、clear操作,会修改该值。扩容也是通过阈值跟size进行比较决定

3 HashMap 数据结构

  • HashMap是一个Node类型的数组,每个元素可以为单节点、链表、红黑树。

  • Java8之前,HashMap的数据结构如下:

数组+链表:链表是为了解决hash冲突

  • Java8,HashMap的数据结构如下:
    数组+链表+红黑树

3.1构造函数

Tips:

  1. 确定加载因子
  2. 根据初始容量参数重新计算扩容阈值(大于或等于初始容量且一定等于2的幂的那个数
    tableSizeFor(initialCapacity):确定扩容阈值:大于或等于初始容量且一定等于2的幂的那个数;比如cap=8则返回8;cap=9则返回16

源码分析如下:

//构造函数一:无参构造函数:加载因子(0.75)和初始容量(16)分别使用默认值
public HashMap() {
	this.loadFactor = DEFAULT_LOAD_FACTOR;
}
//构造函数二:
//指定初始容量,调用HashMap(int initialCapacity, float loadFactor)
public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
//构造函数三:同时指定初始容量和加载因子
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;
    //  确定扩容阈值:大于或等于初始容量且一定等于2的幂的那个数;比如cap=8则返回8;cap=9则返回16
    this.threshold = tableSizeFor(initialCapacity);
}
//构造函数三:创建一个跟参数有相同结构的map
public HashMap(Map<? extends K, ? extends V> m) {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    putMapEntries(m, false);
}

3.2 Node<k,v>分析

tips:一个简单的K-V模型的数据体,提供对key value的set get操作
源码如下:

/**
     * Basic hash bin node, used for most entries.  (See below for
     * TreeNode subclass, an
首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇3、TreeMap源码解析 下一篇AcWing 787.归并排序(Java)

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目