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的数 ...
随机推荐
- Sed命令的使用详细解释
Sed命令的使用详细解释 一:sed命令的简介 sed是一种在线编辑器,它一次处理一行内容.处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处 ...
- 六扇门团队作业 ——《DorMi宿舍管理系统需求分析》
团队作业 --<需求分析> 一.格式描述 这个作业属于哪个课程 <课程的链接> 这个作业要求在哪里 <作业要求的链接> 团队名称 六扇门 这个作业的目标 通过对用户 ...
- POJ1149 PIGS 【最大流 + 构图】
题目链接:http://poj.org/problem?id=1149 PIGS Time Limit: 1000MS Memory Limit: 10000K Total Submissions ...
- 关于quartz定期,起服务时不新增配置文件中的定期问题
关于quartz定期,起服务时不新增配置文件中的定期问题 问题原因:生产环境中起服务,未加载配置文件信息,且quartz连接超时 查找原因发现 由于别人新建了一个定期文件 并将 quartz工厂类的i ...
- 使用pycharm开发web——django2.1.5(三)创建models并进入交互界面shell做一些简单操作
这里model可以认为是数据对象本身 相当于在写java代码时候model目录下创建的实体类,models.py 中可以包含多个实体类,感觉这个操作挺骚的 下面是polls app里面的models, ...
- 什么是文件存储NAS
阿里云文件存储(Network Attached Storage,简称 NAS)是面向阿里云 ECS 实例.E-HPC 和容器服务等计算节点的文件存储服务. 定义 阿里云文件存储 NAS 是一个可共享 ...
- selenium (二)
文件上传: 对于通过input标签实现的上传功能,可以将其看作是一个输入框,即通过send_keys()指定本地文件路径的方式实现文件上传 创建upfile.html文件,代码如下: <html ...
- django API返回中文乱码
renturn HttpResponse(json.dumps(data,ensure_ascii=False))
- Angular6如何引入jQuery-knob
Angular6如何引入jQuery-knob 1.概述 Angular6引入jQuery变得异常简单,请参考https://blog.csdn.net/qq_35321405/article/det ...
- *4.1 所有类型都从System.Object派生