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的数 ...
随机推荐
- LIBS+=(20191017)
1.方式一:(ZC:"LIBPATH"中写路径,"LIBS"中写lib文件名[不带后缀]) LIBPATH += F:/ZC_IDE/VC_3rd/libxml ...
- DecodingGenome(CodeForces-222E)【矩阵快速幂】
题目链接:https://vjudge.net/contest/333591#problem/L 题意:用m个字符构成长度为n的串,其中存在形如“ab”(表示a后不能放置b)的条件约束,问共有多少种构 ...
- C++中如何设计一个类只能在堆或者栈上创建对象,面试题
设计一个类,该类只能在堆上创建对象 将类的构造函数私有,拷贝构造声明成私有.防止别人调用拷贝在栈上生成对象. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建 注意 在堆和栈上创建对象都会调 ...
- celery(芹菜) 异步任务 定时任务 周期任务
什么是celery Celery是一个简单.灵活且可靠的,处理大量消息的分布式系统 专注于实时处理的异步任务队列 同时也支持任务调度 celery架构 celery的架构由三部分组成,消息中间件(me ...
- python学习-10 运算符1
1.加+,减-,乘*,除/ 例如: a = 1 b = 2 c = a + b print(c) 运算结果: 3 Process finished with exit code 0 a = 1 b = ...
- python — 池
1. 池 池分为:进程池.线程池 池:预先的开启固定个数的进程数/线程数,当任务来临的时候,直接提交给已经开好的进程 / 线程,让这个进程 / 线程去执行就可以了. 池节省了进程.线程的开启.关闭.切 ...
- 9.ssh登录慢
修改方式:使用root权限修改ssh的配置文件,vim /etc/ssh/sshd_config增加一行记录:UseDNS no修改GSSAPIAuthentication参数为 no,默认是yesP ...
- 第八章 ZYNQ-MIZ701 软硬调试高级技巧
软件和硬件的完美结合才是SOC的优势和长处,那么开发ZYNQ就需要掌握软件和硬件开发的调试技巧,这样才能同时分析软件或者硬件的运行情况,找到问题,最终解决.那么本章将通过一个简单的例子带大家使用v ...
- T100——程序从标准签出客制后注意r.c和r.l
标准签出客制后,建议到对应4gl目录,客制目录 r.c afap280_01 r.l afap280_01 ALL 常用Shell操作命令: r.c:编译程序,需在4gl路径之下执行,产生的42m会自 ...
- 【Trie】Nikitosh 和异或
[参考博客]: LOJ#10051」「一本通 2.3 例 3」Nikitosh 和异或(Trie [题目链接]: https://loj.ac/problem/10051 [题意]: 找出两个不相交区 ...