JDK源码笔记之HashMap类和HashSet类

1. class 简介

  HashMap和HashSet是使用非常广泛的java集合,其中HashSet本质上就是一个HashMap。数组以整数索引作为下标,而HashMap相当于把任意对象作为下标(中途通过哈希函数转换成哈希地址),实现了常量时间的随机访问,当哈希地址冲突时(机率很小),使用链地址法解决冲突。主要方法有put,get,remove,containsKey,containsValue,keySet,entrySet等方法。

2. class内部原理及特点

  1. 不是线程安全的。
  2. 键和值都允许为null,规定null键对应的哈希地址为0(桶下标)。键不会出现重复,但值可以出现重复。
  3. 内部是一个循环数组Entry[] table,数组中每个元素都是一条单链表,数组默认初始容量为16(数量一定为2的幂,方便快速取余,循环使用数组),最大容量为2^30;默认负载因子为0.75(是时间和空间的折衷),负载因子与容量的乘积为阈值,超过阈值会扩容,然后重构哈希表,创建一个新的Entry数组,将旧Entry[]的非null键值对一个一个重新计算哈希地址复制到新的Entry数组中,代价很高昂。所以可以预估一下所需HashMap的容量大小。
  4. HashMap并没有使用自带的hashCode直接作为数组(桶)的下标,而是通过一个额外的hash函数将其再次打乱,进一步降低哈希地址出现冲突的机率,提高哈希表的性能
  5. 对HashMap进行迭代会遍历整个Entry数组,但是数组中很多位置是空的,当需要考虑迭代的性能时,不要把HashMap容量设置得太大,或者把负载因子设置得太小。
  6. 其迭代器是快速失败的,在其迭代器创建之后调用非迭代器中的方法对容器的内部结构进行修改都会抛出ConcurrentModificationException。
  7. HashSet底层就是一个HashMap,只不过把元素全部保存在键上,值统一用一个PRESENT的Object对象占位,各种方法全部借用了HashMap,这也体现了组合的灵活。
    这里写图片描述

3. class源码细节分析

  先回忆一下数据结构哈希表的创建添加键值对根据键查找键值对取得所有键和所有值的具体做法(用经典的timesXX哈希函数和链地址法):
  创建哈希表:创建一个指定长度的数组,数组类型为链表结点类型,结点包含键、值和指向下一个结点的引用。
  添加键值对
  1、用timesXX算法计算出键的哈希地址作为循环数组的下标,如果下标超过了数组的最大下标,则对长度取余作为下标。
  2、遍历数组当前下标对应的链表,中途将待添加键值对与链表结点的键进行比较,如果相同,说明之前添加过相同的键,则将新值覆盖结点的旧值,结束;如果遍历完成之后没有键没有相同的,说明是哈希码冲突了,此时将键值对添加到链表中。
  根据键查找键值对
  1、用timesXX算法计算出键的哈希地址作为循环数组的下标,如果下标超过了数组的最大下标,则对长度取余作为下标。
  2、遍历数组当前下标对应的链表,找到键相同的结点,接下来可以返回其中的值或者删除此结点。
  取得所有键和所有值
  遍历整个数组,对数组中每个链表进行一一遍历
  
只要了解了哈希表数据结构的内部原理,看JDK中HashMap源码就不会存在任何问题。

基本成员变量
/* 默认初始容量 */
static final int DEFAULT_INITIAL_CAPACITY = 16;
/* 最大容量 */
static final int MAXIMUM_CAPACITY = 1 << 30;
/* 默认负载因子 */
static final float DEFAULT_LOAD_FACTOR = 0.75f;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值