【博学谷学习记录】超强总结,用心分享|Java集合底层原理分析(2/2)(Map)

目录

 

一、Java 集合介绍

二、List

  2.1 ArrayList

  2.2 LinkedList

  2.3 Vector

  2.4 Stack

  2.5 CopyOnWriteArrayList

  2.6 CopyOnWriteArraySet

  2.7 ArrayList 和 Vector 区别

  2.8 ArrayList 与 LinkedList 的区别

三、Map
3.1 HashMap

3.2 HashTable

 

 

三、Map
3.1 HashMap
  HashMap 是以key-value 键值对形式存储数据,允许 key 为 null(多个则覆盖),也允许 value 为 null。底层结构是数组 + 链表 + 红黑树。

主要属性:

  initialCapacity:初始容量,默认 16,2 的 N 次方。
  loadFactor:负载因子,默认 0.75,用于扩容。
  threshold:阈值,等于 initialCapacity * loadFactor,比如:16 * 0.75 = 12。
  size:存放元素的个数,非 Node 数组长度。
Node

复制代码
复制代码
//存储元素的数组
transient Node<K,V>[] table;
//存放元素的个数,非Node数组长度
transient int size;
//记录结构性修改次数,用于快速失败
transient int modCount;
//阈值
int threshold;
//负载因子,默认0.75,用于扩容
final float loadFactor;

 /\*\* \* 静态内部类,存储数据的节点 \*/
static class Node\<K,V\> implements Map.Entry\<K,V\> {
    //节点的hash值
    final int hash;
    //节点的key值
    final K key;
    //节点的value值
    V value;
    //下一个节点的引用
    Node<K,V> next;
}
复制代码
复制代码

**数据结构:**数组 + 单链表,Node 结构:hash|key|value|next

**只允许一个 key 为 Null(多个则覆盖),但允许多个 value 为 Null **

  查询、插入、删除效率都高(集成了数组和单链表的特性)
** * 默认的初始化大小为 16,之后每次扩充为原来的 2 倍
  线程不安全
使用场景:

  快速增删改查
  随机存取
  缓存
  哈希冲突的解决方案:

    开放定址法
    再散列函数法
    链地址法(拉链法,常用)
put() 存储的流程(Java 8):

  (1)计算待新增数据 key 的 hash 值;
  (2)判断 Node[] 数组是否为空或者数据长度为 0 的情况,则需要进行初始化;
  (3)根据 hash 值通过位运算定计算出 Node 数组的下标,判断该数组第一个 Node 节点是否有数据,如果没有数据,则插入新值;
  (4)如果有数据,则根据具体情况进行操作,如下:
    1.如果该 Node 结点的 key(即链表头结点)与待新增的 key 相等(== 或者 equals),则直接覆盖值,最后返回旧值;
    2.如果该结构是树形,则按照树的方式插入新值;
    3.如果是链表结构,则判断链表长度是否大于阈值 8,如果 >=8 并且数组长度 >=64 才转为红黑树,如果 >=8 并且数组长度 < 64 则进行扩容;
    4.如果不需要转为红黑树,则遍历链表,如果找到 key 和 hash 值同时相等,则进行覆盖返回旧值,如果没有找到,则将新值插入到链表的最后面(尾插法);
    5.判断数组长度是否大于阈值,如果是则进入扩容阶段。
resize() 扩容的流程(Java 8):

  扩容过程比较复杂, 迁移算法与 Java 7 不一样,Java 8 不需要每个元素都重新计算 hash,迁移过程中元素的位置要么是在原位置,要么是在原位置再移动 2 次幂的位置。

get() 查询的流程(Java 8):

  根据 put() 方法的方式计算出数组的下标;
  遍历数组下标对应的链表,如果找到 key 和 hash 值同时相等就返回对应的值,否则返回 null。
get() 注意事项:Java 8 没有把 key 为 null 放到数组 table[0] 中。

remove() 删除的流程(Java 8):

  根据 get() 方法的方式计算出数组的下标,即定位到存储删除元素的 Node 结点;
  如果待删结点是头节点,则用它的 next 结点顶替它作为头节点;
  如果待删结点是红黑树结点,则直接调用红黑树的删除方法进行删除;
  如果待删结点是链表中的一个节点,则用待删除结点的前一个节点的 next 属性指向它的 next 结点;
  如果删除成功则返回被删结点的 value,否则返回 null。
remove() 注意事项:删除单个 key,注意返回是的键值对中的 value。

为什么使用位运算(&)来代替取模运算(%):

  效率高,位运算直接对内存数据进行操作,不需转成十进制,因此处理速度非常快;
  可以解决负数问题,比如:-17 % 10 = -7。
HashMap 在 Java 7 和 Java 8 中的区别:

  (1)存放数据的结点名称不同,作用都一样,存的都是 hashcode、key、value、next 等数据:
    Java 7:使用 Entry 存放数据
    Java 8:改名为 Node
  (2)定位数组下标位置方法不同:
    Java 7:计算 key 的 hash,将 hash 值进行了四次扰动,再进行取模得出;
    Java 8:计算 key 的 hash,将 hash 值进行高 16 位异或低 16 位,再进行与运算得出。
  (3)扩容算法不同:
    Java 7:扩容要重新计算 hash
    Java 8:不用重新计算
(4)put 方法插入链表位置不同:
    Java 7:头插法
    Java 8:尾插法
(5)Java 8 引入了红黑树,当链表长度 >=8 时,并且同时数组的长度 >=64 时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高 HashMap 的性能。
3.2 HashTable
和 HashMap 一样,Hashtable 也是一个哈希散列表,Hashtable 继承于 Dictionary,使用重入锁 Synchronized 实现线程安全,key 和 value 都不允许为 Null。HashTable 已被高性能的 ConcurrentHashMap 代替。

主要属性:

  initialCapacity:初始容量,默认 11。
  loadFactor:负载因子,默认 0.75。
  threshold:阈值。
  modCount:记录结构性修改次数,用于快速失败。

复制代码
复制代码
//真正存储数据的数组
private transient Entry<?,?>[] table;
//存放元素的个数,非Entry数组长度
private transient int count;
//阈值
private int threshold;
//负载因子,默认0.75
private float loadFactor;
//记录结构性修改次数,用于快速失败
private transient int modCount = 0;

/\*\* \* 静态内部类,存储数据的节点 \*/
private static class Entry\<K,V\> implements Map.Entry\<K,V\> {
    //节点的hash值
    final int hash;
    //节点的key值
    final K key;
    //节点的value值
    V value;
    //下一个节点的引用
    Entry<K,V> next;
}
复制代码
复制代码

快速失败原理是在并发场景下进行遍历操作时,如果有另外一个线程对它执行了写操作,此时迭代器可以发现并抛出 ConcurrentModificationException,而不需等到遍历完后才报异常。

**数据结构:**链表的数组,数组 + 链表,Entry 结构:hash|key|value|next

特征:

  key 和 value 都不允许为 Null;
  HashTable 默认的初始大小为 11,之后每次扩充为原来的 2 倍;
线程安全。
原理:

与 HashMap 不一样的流程是定位数组下标逻辑,HashTable 是在 key.hashcode() 后使用取模,HashMap 是位运算。HashTable 是 put() 之前进行判断是否扩容 resize(),而 HashMap 是 put() 之后扩容。

posted @   后来--  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示