Java:这是一份详细&全面的HashMap 1,高并发下Java程序的GC问题排查

System.out.println(map.get(key));
}

System.out.println("----------");

// 2.2 通过迭代器:先获得key的Iterator,再循环遍历
Iterator iter2 = keySet.iterator();
String key = null;
while (iter2.hasNext()) {
key = (String)iter2.next();
System.out.print(key);
System.out.println(map.get(key));
}

// 方法3:获得value的Set集合 再遍历
System.out.println(“方法3”);

// 1. 获得value的Set集合
Collection valueSet = map.values();

// 2. 遍历Set集合,从而获取value
// 2.1 获得values 的Iterator
Iterator iter3 = valueSet.iterator();
// 2.2 通过遍历,直接获取value
while (iter3.hasNext()) {
System.out.println(iter3.next());
}

}

}

// 注:对于遍历方式,推荐使用针对 key-value对(Entry)的方式:效率高
// 原因:
// 1. 对于 遍历keySet 、valueSet,实质上 = 遍历了2次:1 = 转为 iterator 迭代器遍历、2 = 从 HashMap 中取出 key 的 value 操作(通过 key 值 hashCode 和 equals 索引)
// 2. 对于 遍历 entrySet ,实质 = 遍历了1次 = 获取存储实体Entry(存储了key 和 value )

  • 运行结果

方法1
Java2
iOS3
数据挖掘4
Android1
产品经理5

Java2
iOS3
数据挖掘4
Android1
产品经理5
方法2
Java2
iOS3
数据挖掘4
Android1
产品经理5

Java2
iOS3
数据挖掘4
Android1
产品经理5
方法3
2
3
4
1
5

下面,我们按照上述的使用过程,对一个个步骤进行源码解析


4. 基础知识:HashMap中的重要参数(变量)

  • 在进行真正的源码分析前,先讲解HashMap中的重要参数(变量)
  • HashMap中的主要参数 = 容量、加载因子、扩容阈值
  • 具体介绍如下

// 1. 容量(capacity): HashMap中数组的长度
// a. 容量范围:必须是2的幂 & <最大容量(2的30次方)
// b. 初始容量 = 哈希表创建时的容量
// 默认容量 = 16 = 1<<4 = 00001中的1向左移4位 = 10000 = 十进制的2^4=16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
// 最大容量 = 2的30次方(若传入的容量过大,将被最大值替换)
static final int MAXIMUM_CAPACITY = 1 << 30;

// 2. 加载因子(Load factor):HashMap在其容量自动增加前可达到多满的一种尺度
// a. 加载因子越大、填满的元素越多 = 空间利用率高、但冲突的机会加大、查找效率变低(因为链表变长了)
// b. 加载因子越小、填满的元素越少 = 空间利用率小、冲突的机会减小、查找效率高(链表不长)
// 实际加载因子
final float loadFactor;
// 默认加载因子 = 0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;

// 3. 扩容阈值(threshold):当哈希表的大小 ≥ 扩容阈值时,就会扩容哈希表(即扩充HashMap的容量)
// a. 扩容 = 对哈希表进行resize操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数
// b. 扩容阈值 = 容量 x 加载因子
int threshold;

// 4. 其他
// 存储数据的Entry类型 数组,长度 = 2的幂
// HashMap的实现方式 = 拉链法,Entry数组上的每个元素本质上是一个单向链表
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
// HashMap的大小,即 HashMap中存储的键值对的数量
transient int size;

  • 参数示意图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m9jPu7o9-1637302417037)(https://user-gold-cdn.xitu.io/2018/3/14/16222058940df293?imageView2/0/w/1280/h/960/ignore-error/1)]

  • 此处 详细说明 加载因子

示意图


5. 源码分析

  • 本次的源码分析主要是根据 使用步骤 进行相关函数的详细分析
  • 主要分析内容如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KbKaVqCs-1637302417135)(https://user-gold-cdn.xitu.io/2018/3/14/16222058bc667a46?imageView2/0/w/1280/h/960/ignore-error/1)]

  • 下面,我将对每个步骤内容的主要方法进行详细分析

步骤1:声明1个 HashMap的对象

/**

  • 函数使用原型
    */
    Map<String,Integer> map = new HashMap<String,Integer>();

/**

  • 源码分析:主要是HashMap的构造函数 = 4个
  • 仅贴出关于HashMap构造函数的源码
    */
    public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable{

// 省略上节阐述的参数

/**

  • 构造函数1:默认构造函数(无参)
  • 加载因子 & 容量 = 默认 = 0.75、16
    */
    public HashMap() {
    // 实际上是调用构造函数3:指定“容量大小”和“加载因子”的构造函数
    // 传入的指定容量 & 加载因子 = 默认
    this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }

/**

  • 构造函数2:指定“容量大小”的构造函数
  • 加载因子 = 默认 = 0.75 、容量 = 指定大小
    */
    public HashMap(int initialCapacity) {
    // 实际上是调用指定“容量大小”和“加载因子”的构造函数
    // 只是在传入的加载因子参数 = 默认加载因子
    this(initialCapacity, DEFAULT_LOAD_FACTOR);

}

/**

  • 构造函数3:指定“容量大小”和“加载因子”的构造函数
  • 加载因子 & 容量 = 自己指定
    */
    public HashMap(int initialCapacity, float loadFactor) {

// HashMap的最大容量只能是MAXIMUM_CAPACITY,哪怕传入的 > 最大容量
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;

// 设置 加载因子
this.loadFactor = loadFactor;
// 设置 扩容阈值 = 初始容量
// 注:此处不是真正的阈值,是为了扩展table,该阈值后面会重新计算,下面会详细讲解
threshold = initialCapacity;

init(); // 一个空方法用于未来的子对象扩展
}

/**

  • 构造函数4:包含“子Map”的构造函数
  • 即 构造出来的HashMap包含传入Map的映射关系
  • 加载因子 & 容量 = 默认
    */

public HashMap(Map<? extends K, ? extends V> m) {

// 设置容量大小 & 加载因子 = 默认
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);

// 该方法用于初始化 数组 & 阈值,下面会详细说明
inflateTable(threshold);

// 将传入的子Map中的全部元素逐个添加到HashMap中
putAllForCreate(m);
}
}

  • 注:
  1. 此处仅用于接收初始容量大小(capacity)、加载因子(Load factor),但仍无真正初始化哈希表,即初始化存储数组table
  2. 此处先给出结论:真正初始化哈希表(初始化存储数组table)是在第1次添加键值对时,即第1次调用put()时。下面会详细说明

至此,关于HashMap的构造函数讲解完毕。


步骤2:向HashMap添加数据(成对 放入 键 - 值对)

  • 添加数据的流程如下

注:为了让大家有个感性的认识,只是简单的画出存储流程,更加详细 & 具体的存储流程会在下面源码分析中给出

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FFnqngtG-1637302417136)(https://user-gold-cdn.xitu.io/2018/3/14/16222058bda92a19?imageView2/0/w/1280/h/960/ignore-error/1)]

  • 源码分析

/**

  • 函数使用原型
    */
    map.put(“Android”, 1);
    map.put(“Java”, 2);
    map.put(“iOS”, 3);
    map.put(“数据挖掘”, 4);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值