HashMap源码分析一
private transient Entry table[]; private transient int count; private int threshold; private float loadFactor; private transient int modCount = 0; // 构造方法一
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Initial Capacity: "+
initialCapacity);
if (loadFactor <= 0)
throw new IllegalArgumentException("Illegal Load factor: "+
loadFactor);
if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry[initialCapacity];
threshold = (int)(initialCapacity * loadFactor);
}
// 构造方法二
public HashMap(int initialCapacity) {
this(initialCapacity, 0.75f);
}
// 构造方法三
public HashMap() {
this(101, 0.75f);
}
// 构造方法四
public HashMap(Map t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}
// 查看HashMap存放数,count是HashMap的存放数属性
public int size() {
return count;
} // 查看HashMap存放数是否为空,count==0表示存放数为空
public boolean isEmpty() {
return count == 0;
}
// HashMap内部实体类
private static class Entry implements Map.Entry {
int hash; // 存入HashMap key值的hashCode值
Object key; // 存入HashMap的key值
Object value; // 存入HashMap的value值
Entry next; // 存入HashMap中下一个值,这里是为产生Hash碰撞后,链表结构准备的,单向链表 // 构造方法
Entry(int hash, Object key, Object value, Entry next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
} // 对象克隆方法
protected Object clone() {
return new Entry(hash, key, value,
(next==null ? null : (Entry)next.clone()));
} public Object getKey() {
return key;
} public Object getValue() {
return value;
} // 设置value值,并返回老的value值
public Object setValue(Object value) {
Object oldValue = this.value;
this.value = value;
return oldValue;
} // 判断实体对象的相等方法;只有在实体的key和value都相等时,才相等
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o; return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
(value==null ? e.getValue()==null : value.equals(e.getValue()));
} // hashCode值,实体的hashCode值为key的hashCode和value的hashCode的异或值
public int hashCode() {
return hash ^ (value==null ? 0 : value.hashCode());
} public String toString() {
return key+"="+value;
}
}
// Iterator的类型
private static final int KEYS = 0; // Iterator对key进行操作
private static final int VALUES = 1;// Iterator对value进行操作
private static final int ENTRIES = 2;// iterator对entry进行操作 // HashMap对Iterator接口的实现
private class HashIterator implements Iterator {
Entry[] table = HashMap.this.table;
int index = table.length;
Entry entry = null; // 临时变量,存放hasNext方法时,遍历到的数组位链表对象
Entry lastReturned = null; // 临时变量,存放next方法时,遍历到的对象;为remove方法中准备
int type; // iterator的类型 // 存放HashMap修改的次数
private int expectedModCount = modCount; // 构造方法,入参为设定Iterator的类型
HashIterator(int type) {
this.type = type;
} // 判断容器内是否有值
public boolean hasNext() {
while (entry==null && index>0)
entry = table[--index]; return entry != null;
} // 获取下一个值
public Object next() {
// 遍历过程中,HashMap是不允许有改动,否则抛出异常ConcurrentModificationException
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
// 遍历过程中,处理那种没有先调用hasNext方法的情况
while (entry==null && index>0)
entry = table[--index];
// 如果entry不为null,处理entry
if (entry != null) {
// entry存放在临时变量lastReturned
Entry e = lastReturned = entry;
// entry存放链表的下一个节点,为下一次调用next方法做准备
entry = e.next;
// 根据type的类型,返回不同的值
return type == KEYS ? e.key : (type == VALUES ? e.value : e);
}
throw new NoSuchElementException();
} // 移除实体对象
public void remove() {
// remove调用之前,需要先调用hasNext方法,否则lastReturned=null,抛出异常
if (lastReturned == null)
throw new IllegalStateException();
// 移除操作,HashMap也不希望在其他地方有修改
if (modCount != expectedModCount)
throw new ConcurrentModificationException(); Entry[] tab = HashMap.this.table;
// 找到lastReturned对应数组的位置
int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length; // 找到对应数组的位置后,删除对应链表中的lastReturned值,下面是链表的操作
for (Entry e = tab[index], prev = null; e != null;
prev = e, e = e.next) {
if (e == lastReturned) {
modCount++;
expectedModCount++;
if (prev == null)
tab[index] = e.next;
else
prev.next = e.next;
count--;
lastReturned = null;
return;
}
}
throw new ConcurrentModificationException();
}
}
// 判断HashMap是否包含key值
public boolean containsKey(Object key) {
Entry tab[] = table; // key值不为null
if (key != null) {
// 通过key计算出key对应数组table的下标,其实这里是可以封装成一个方法的,多个地方都要用到,后面jdk的版本都有改进
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
// 查找链表,判断key是否存在
for (Entry e = tab[index]; e != null; e = e.next)
if (e.hash==hash && key.equals(e.key))
return true;
} else { // key为null
// 查找链表,判断key是否存在
for (Entry e = tab[0]; e != null; e = e.next)
if (e.key==null)
return true;
} return false;
} // 判断HashMap是否包含value值
// 这个方法实际效率是很低的,需要先遍历数组,在一个个遍历链表,没有用到hash特性
public boolean containsValue(Object value) {
Entry tab[] = table;
// value值为null
if (value==null) {
// 遍历数组
for (int i = tab.length ; i-- > 0 ;)
// 遍历链表
for (Entry e = tab[i] ; e != null ; e = e.next)
if (e.value==null)
return true;
} else {// value值不为null
// 遍历数组
for (int i = tab.length ; i-- > 0 ;)
// 遍历链表
for (Entry e = tab[i] ; e != null ; e = e.next)
if (value.equals(e.value))
return true;
} return false;
} // 获取key对应的value值
public Object get(Object key) {
Entry tab[] = table;
// key不为null
if (key != null) {
// key的hashCode值
int hash = key.hashCode();
// key的hashCode计算出的hash值
int index = (hash & 0x7FFFFFFF) % tab.length;
// 通过index定位到数组,然后遍历对应的链表结构
for (Entry e = tab[index]; e != null; e = e.next)
if ((e.hash == hash) && key.equals(e.key))
return e.value;
} else {
// 如果key为null,直接遍历对应的链表结构
for (Entry e = tab[0]; e != null; e = e.next)
if (e.key==null)
return e.value;
} return null;
} // 往HashMap中存放key和value
public Object put(Object key, Object value) { Entry tab[] = table;
int hash = 0;
int index = 0; // 这部分操作是当数组下标的对应位置已经有值,并且
// key不为null
if (key != null) {
// 获得key对应数组的下标
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
// 在数组对应的下标有值的情况下
// key已经存在,把新的value代替老的value,并返回老的value
for (Entry e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && key.equals(e.key)) {
Object old = e.value;
e.value = value;
return old;
}
}
} else { // key为null,数组的下标是确定的,为0
// 在数组对应的下标有值的情况下
// key已经存在,把新的value代替老的value,并返回老的value
for (Entry e = tab[0] ; e != null ; e = e.next) {
if (e.key == null) {
Object old = e.value;
e.value = value;
return old;
}
}
} // HashMap的修改次数加一
modCount++;
// 如果数组的使用长度大于或等于阀值threshold后
if (count >= threshold) {
// 扩展数组
rehash(); // 重新赋值tab
tab = table;
// 重新计算index
index = (hash & 0x7FFFFFFF) % tab.length;
} // 在原HashMap中,key没找到对应的value时
// 这里先构造一个实体,把实体的next属性指向数组下标index对应的位置
Entry e = new Entry(hash, key, value, tab[index]);
// 数组下标对应的位置赋值实体e
tab[index] = e;
// 实体数量加一
count++;
return null;
} // 推展数组
private void rehash() {
// 原数组长度
int oldCapacity = table.length;
// 原数组
Entry oldMap[] = table; // 新数组长度,这里数组的增长策略是2倍加一的增
int newCapacity = oldCapacity * 2 + 1;
// 重新创建数组
Entry newMap[] = new Entry[newCapacity]; // HashMap的修改次数加一
modCount++;
// 更新HashMap的扩展数组的阀值
threshold = (int)(newCapacity * loadFactor);
// 赋值新数组到HashMap的数组
// 这个地方存在并发问题,因为此时数组虽然创建了,但是实体值还是没有迁移过来的。
// 如果此时有人读取value值,可能会返回为空
table = newMap; // 把老数组里的值迁移到新数组里
// 遍历数组
for (int i = oldCapacity ; i-- > 0 ;) {
// 遍历链表
for (Entry old = oldMap[i] ; old != null ; ) {
Entry e = old;
old = old.next; int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = newMap[index];
newMap[index] = e;
}
}
} // 移除实体
public Object remove(Object key) {
Entry tab[] = table;
// key不为null
if (key != null) {
// 获得key对应的hash值和对应的数组下标
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length; // 根据对应的数组下标,找到链表对象
// 遍历链表
for (Entry e = tab[index], prev = null; e != null;
prev = e, e = e.next) {
if ((e.hash == hash) && key.equals(e.key)) {
modCount++;
if (prev != null)
prev.next = e.next;
else
tab[index] = e.next; count--;
Object oldValue = e.value;
e.value = null;
return oldValue;
}
}
} else {// key为null,对应的数组下标为0
// 根据对应的数组下标,找到链表对象
// 遍历链表
for (Entry e = tab[0], prev = null; e != null;
prev = e, e = e.next) {
if (e.key == null) {
modCount++;
if (prev != null)
prev.next = e.next;
else
tab[0] = e.next; count--;
Object oldValue = e.value;
e.value = null;
return oldValue;
}
}
} return null;
} // 批量添加
public void putAll(Map t) {
Iterator i = t.entrySet().iterator();
while (i.hasNext()) {
Map.Entry e = (Map.Entry) i.next();
put(e.getKey(), e.getValue());
}
} // 清除数组内容
public void clear() {
Entry tab[] = table;
modCount++;
for (int index = tab.length; --index >= 0; )
tab[index] = null;
count = 0;
}
HashMap源码分析一的更多相关文章
- 【JAVA集合】HashMap源码分析(转载)
原文出处:http://www.cnblogs.com/chenpi/p/5280304.html 以下内容基于jdk1.7.0_79源码: 什么是HashMap 基于哈希表的一个Map接口实现,存储 ...
- Java中HashMap源码分析
一.HashMap概述 HashMap基于哈希表的Map接口的实现.此实现提供所有可选的映射操作,并允许使用null值和null键.(除了不同步和允许使用null之外,HashMap类与Hashtab ...
- JDK1.8 HashMap源码分析
一.HashMap概述 在JDK1.8之前,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的节点都存储在一个链表里.但是当位于一个桶中的元素较多,即hash值相等的元素较多时 ...
- HashMap源码分析和应用实例的介绍
1.HashMap介绍 HashMap 是一个散列表,它存储的内容是键值对(key-value)映射.HashMap 继承于AbstractMap,实现了Map.Cloneable.java.io.S ...
- 【Java】HashMap源码分析——常用方法详解
上一篇介绍了HashMap的基本概念,这一篇着重介绍HasHMap中的一些常用方法:put()get()**resize()** 首先介绍resize()这个方法,在我看来这是HashMap中一个非常 ...
- 【Java】HashMap源码分析——基本概念
在JDK1.8后,对HashMap源码进行了更改,引入了红黑树.在这之前,HashMap实际上就是就是数组+链表的结构,由于HashMap是一张哈希表,其会产生哈希冲突,为了解决哈希冲突,HashMa ...
- Java BAT大型公司面试必考技能视频-1.HashMap源码分析与实现
视频通过以下四个方面介绍了HASHMAP的内容 一. 什么是HashMap Hash散列将一个任意的长度通过某种算法(Hash函数算法)转换成一个固定的值. MAP:地图 x,y 存储 总结:通过HA ...
- Java源码解析——集合框架(五)——HashMap源码分析
HashMap源码分析 HashMap的底层实现是面试中问到最多的,其原理也更加复杂,涉及的知识也越多,在项目中的使用也最多.因此清晰分析出其底层源码对于深刻理解其实现有重要的意义,jdk1.8之后其 ...
- HashMap源码分析(史上最详细的源码分析)
HashMap简介 HashMap是开发中使用频率最高的用于映射(键值对 key value)处理的数据结构,我们经常把hashMap数据结构叫做散列链表: ObjectI entry<Key, ...
- HashMap 源码分析 基于jdk1.8分析
HashMap 源码分析 基于jdk1.8分析 1:数据结构: transient Node<K,V>[] table; //这里维护了一个 Node的数组结构: 下面看看Node的数 ...
随机推荐
- Java中的一些关键字:static,final,和abstract,interface,以及访问修饰符说明
1.关键字可以修饰的说明: 关键字 属性(是否可修饰) 方法(是否可修饰) 类(是否可修饰) static 是 是 是 final 是 是 是 abstract 否 是 是 2.关键字的意义: 关键字 ...
- 通过js获取本机的IP地址
参考链接:https://blog.csdn.net/qq_39327418/article/details/90052668
- 磁盘冗余列阵Raid技术知识与实践
Raid介绍 什么是Raid? Raid级别介绍 Raid 技术分类 RAID分为两类: 软RAID, 系统层面实现的,性能差. 硬RAID, 硬件层面实现的,性能好. 主板板载RAID: 功能弱 ...
- docker镜像内没有vim
问题: 我们在容器中找不到vim等命令 原因: 镜像制作的时候没把这些东西加进去 解决: 用apt update更新源之后再安装vim apt update apt-get install - ...
- BIO和NIO实现文件复制
普通文件复制 public void copyFile() throws Exception{ FileInputStream fis=new FileInputStream("C:\\Us ...
- UWP笔记-消息弹窗自动淡出
为了让用户有个更好的UI交互,可以增加自动淡出的消息弹窗,例如:网易云音乐UWP,切换播放模式时,出现的类似消息提示. 右键项目,添加用户控件 UserControlDemo.xaml: <Us ...
- 《MIT 6.828 Lab 1 Exercise 11》实验报告
本实验的网站链接:MIT 6.828 Lab 1 Exercise 11. 题目 The above exercise should give you the information you need ...
- 初识RedisCluster集群
为什么需要Redis集群 需要提高更大的并发量 Redis官方提出拥有10万QPS的请求量 如果业务需要Redis拥有100万的QPS 可以通过集群来提升并发量. 需要存储更大的数据量 一般服务器的机 ...
- Sumitomo Mitsui Trust Bank Programming Contest 2019 Task F. Interval Running
Link. There is a nice approach to this problem that involves some physical insight. In the following ...
- EffectiveC++
宁可以编译器替换预处理器 define 不被视为语言的一部分,因此也就有可能在预处理阶段被优化掉,导致相关变量出现错误. #define ASPECT_RATIO 1.63 //可以尝试将其替换为 c ...