HashMap(JDK1,java编程基础实验报告小结

HashMap是Java中常用的Map实现,它具有键不可重复、线程不安全等特点。JDK1.8之前,HashMap采用拉链法解决哈希冲突,当链表长度超过8时,会转换为红黑树以提高查找效率。JDK1.8在扩容和哈希计算上进行了优化,减少了冲突。扩容时,新容量为原容量的2倍,元素根据新的哈希规则重新分布,降低冲突概率。HashMap的hash()方法通过异或和位运算确保高位参与哈希计算,降低碰撞率。
摘要由CSDN通过智能技术生成

特点

HashMap 可以说是我们使用最多的 Map 集合,它有以下特点:

  • 键不可重复,值可以重复

  • 底层哈希表

  • 线程不安全

  • 允许key为null,value也可以为null

数据结构

在Java中,保存数据有两种比较简单的数据结构:数组和链表。数组的特点是:寻址容易,插入和删除困难;链表的特点是:寻址困难,但插入和删除容易;所以我们将数组和链表结合在一起,发挥两者各自的优势,使用一种叫做拉链法的方式可以解决哈希冲突。

JDK1.8之前

JDK1.8之前采用的是拉链法。拉链法:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。

在这里插入图片描述

JDK1.8之后

相比于之前的版本,jdk1.8在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。

在这里插入图片描述

JDK1.7 VS JDK1.8 比较

JDK1.8主要解决或优化了一下问题:

  1. resize 扩容优化

  2. 引入了红黑树,目的是避免单条链表过长而影响查询效率,红黑树算法请参考

  3. 解决了多线程死循环问题,但仍是非线程安全的,多线程时可能会造成数据丢失问题。

| 不同 | JDK 1.7 | JDK 1.8 |

| :-- | :-- | :-- |

| 存储结构 | 数组 + 链表 | 数组 + 链表 + 红黑树 |

| 初始化方式 | 单独函数:inflateTable() | 直接集成到了扩容函数resize()中 |

| hash值计算方式 | 扰动处理 = 9次扰动 = 4次位运算 + 5次异或运算 | 扰动处理 = 2次扰动 = 1次位运算 + 1次异或运算 |

| 存放数据的规则 | 无冲突时,存放数组;冲突时,存放链表 | 无冲突时,存放数组;冲突 & 链表长度 < 8:存放单链表;冲突 & 链表长度 > 8:树化并存放红黑树 |

| 插入数据方式 | 头插法(先讲原位置的数据移到后1位,再插入数据到该位置) | 尾插法(直接插入到链表尾部/红黑树) |

| 扩容后存储位置的计算方式 | 全部按照原来方法进行计算(即hashCode ->> 扰动函数 ->> (h&length-1)) | 按照扩容后的规律计算(即扩容后的位置=原位置 or 原位置 + 旧容量) |

继承关系图


HashMap继承关系图

HashMap继承抽象类AbstractMap,实现Map接口。除此之外,它还实现了两个标识型接口,这两个接口都没有任何方法,仅作为标识表示实现类具备某项功能。Cloneable表示实现类支持克隆,java.io.Serializable则表示支持序列化。

成员变量


//默认初始化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;

//HashMap结构修改的次数,结构修改是指更改HashMap中的映射数或以其他方式修改其内部结构(例如,rehash的修改)。该字段用于在Collection-views上快速生成迭代器。

transient int modCount;

//Node数组下一次扩容的临界值,第一次为160.75=12(容量负载因子)

int threshold;

//负载因子

final float loadFactor;

//map中包含的键值对的数量

transient int size;

//表数据,即Node键值对数组,Node是单向链表,它实现了Map.Entry接口,总是2的幂次倍

//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>。

transient Node<K,V>[] table;

//存放具体元素的集,可用于遍历map集合

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

capacity、threshold和loadFactor之间的关系:

  • capacity table的容量,默认容量是16

  • threshold table扩容的临界值

  • loadFactor 负载因子,一般 threshold = capacity * loadFactor,默认的负载因子0.75是对空间和时间效率的一个平衡选择,建议大家不要修改。

构造方法


<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值