hashmap的底层实现
HashMap的底层实现都是数组+链表结构实现的,添加、删除、获取元素都是先计算hash值,根据hash值和table.length计算出index也就是table的数组的下标,然后进行相应的操作。
不过HashMap和HashTable计算hash的方法不同:
HashMap是直接用key的hashcode对table数组长度取模;而HashMap则是对key的hashcode进行两次hash,以获得更好的散列值,然后再对table数组的长度取模。
具体方法的实现:
HashMap默认初始化时会创建一个容量为16的Entry数组,默认加载因子为0.75,同时设置临界值16*0.75;
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
table = new Entry[DEFAULT_INITIAL_CAPACITY];
init();
}
对于put方法:
HashMap会对null值key进行特殊处理,总是放到table[0]位置
put过程是先计算hash然后通过hash与table.length取摸计算index值,然后将key放到table[index]位置,当table[index]已存在其它元素时,会在table[index]位置形成一个链表,将新添加的元素放在table[index],原来的元素通过Entry的next进行链接,这样以链表形式解决hash冲突问题,当元素数量达到临界值(capactiy*factor)时,则进行扩容,是table数组长度变为table.length*2;
对于get方法:
同样当key为null时会进行特殊处理,在table[0]的链表上查找key为null的元素
get的过程是先计算hash然后通过hash与table.length取摸计算index值,然后遍历table[index]上的链表,直到找到key,然后返回
对于remove方法:
remove方法和put get类似,计算hash,计算index,然后遍历查找,将找到的元素从table[index]链表移除
对于resize方法:
resize方法在hashmap中并没有公开,这个方法实现了非常重要的hashmap扩容,具体过程为:先创建一个容量为table.length*2的新table,修改临界值,然后把table里面元素计算hash值并使用hash与table.length*2重新计算index放入到新的table里面,这里需要注意下是用每个元素的hash全部重新计算index,而不是简单的把原table对应index位置元素简单的移动到新table对应位置
对于containsKey方法:
containsKey方法是先计算hash然后使用hash和table.length取摸得到index值,遍历table[index]元素查找是否包含key相同的值
简单的MyHashMap:
public class MyHashMap { //默认初始化大小 16
private static final int DEFAULT_INITIAL_CAPACITY = 16;
//默认负载因子 0.75
private static final float DEFAULT_LOAD_FACTOR = 0.75f; //临界值
private int threshold; //元素个数
private int size; //扩容次数
private int resize; private HashEntry[] table; public MyHashMap() {
table = new HashEntry[DEFAULT_INITIAL_CAPACITY];
threshold = (int) (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
size = 0;
} private int index(Object key) {
//根据key的hashcode和table长度取模计算key在table中的位置
return key.hashCode() % table.length;
} public void put(Object key, Object value) {
//key为null时需要特殊处理,为简化实现忽略null值
if (key == null) return;
int index = index(key); //遍历index位置的entry,若找到重复key则更新对应entry的值,然后返回
HashEntry entry = table[index];
while (entry != null) {
if (entry.getKey().hashCode() == key.hashCode() && (entry.getKey() == key || entry.getKey().equals(key))) {
entry.setValue(value);
return;
}
entry = entry.getNext();
}
//若index位置没有entry或者未找到重复的key,则将新key添加到table的index位置
add(index, key, value);
} private void add(int index, Object key, Object value) {
//将新的entry放到table的index位置第一个,若原来有值则以链表形式存放
HashEntry entry = new HashEntry(key, value, table[index]);
table[index] = entry;
//判断size是否达到临界值,若已达到则进行扩容,将table的capacicy翻倍
if (size++ >= threshold) {
resize(table.length * 2);
}
} private void resize(int capacity) {
if (capacity <= table.length) return; HashEntry[] newTable = new HashEntry[capacity];
//遍历原table,将每个entry都重新计算hash放入newTable中
for (int i = 0; i < table.length; i++) {
HashEntry old = table[i];
while (old != null) {
HashEntry next = old.getNext();
int index = index(old.getKey());
old.setNext(newTable[index]);
newTable[index] = old;
old = next;
}
}
//用newTable替table
table = newTable;
//修改临界值
threshold = (int) (table.length * DEFAULT_LOAD_FACTOR);
resize++;
} public Object get(Object key) {
//这里简化处理,忽略null值
if (key == null) return null;
HashEntry entry = getEntry(key);
return entry == null ? null : entry.getValue();
} public HashEntry getEntry(Object key) {
HashEntry entry = table[index(key)];
while (entry != null) {
if (entry.getKey().hashCode() == key.hashCode() && (entry.getKey() == key || entry.getKey().equals(key))) {
return entry;
}
entry = entry.getNext();
}
return null;
} public void remove(Object key) {
if (key == null) return;
int index = index(key);
HashEntry pre = null;
HashEntry entry = table[index];
while (entry != null) {
if (entry.getKey().hashCode() == key.hashCode() && (entry.getKey() == key || entry.getKey().equals(key))) {
if (pre == null) table[index] = entry.getNext();
else pre.setNext(entry.getNext());
//如果成功找到并删除,修改size
size--;
return;
}
pre = entry;
entry = entry.getNext();
}
} public boolean containsKey(Object key) {
if (key == null) return false;
return getEntry(key) != null;
} public int size() {
return this.size;
} public void clear() {
for (int i = 0; i < table.length; i++) {
table[i] = null;
}
this.size = 0;
} @Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(String.format("size:%s capacity:%s resize:%s\n\n", size, table.length, resize));
for (HashEntry entry : table) {
while (entry != null) {
sb.append(entry.getKey() + ":" + entry.getValue() + "\n");
entry = entry.getNext();
}
}
return sb.toString();
}
} class HashEntry {
private final Object key;
private Object value;
private HashEntry next; public HashEntry(Object key, Object value, HashEntry next) {
this.key = key;
this.value = value;
this.next = next;
} public Object getKey() {
return key;
} public Object getValue() {
return value;
} public void setValue(Object value) {
this.value = value;
} public HashEntry getNext() {
return next;
} public void setNext(HashEntry next) {
this.next = next;
}
}
hashmap的底层实现的更多相关文章
- 深入解析Java对象的hashCode和hashCode在HashMap的底层数据结构的应用
转自:http://kakajw.iteye.com/blog/935226 一.java对象的比较 等号(==): 对比对象实例的内存地址(也即对象实例的ID),来判断是否是同一对象实例:又可以说是 ...
- HashMap的底层实现原理
HashMap的底层实现原理1,属性static final int MAX_CAPACITY = 1 << 30;//1073741824(十进制)0100000000000000000 ...
- HashMap的底层原理(jdk1.7.0_79)
前言 在Java中我们最常用的集合类毫无疑问就是Map,其中HashMap作为Map最重要的实现类在我们代码中出现的评率也是很高的. 我们对HashMap最常用的操作就是put和get了,那么你知道它 ...
- HashMap的底层结构和原理
http://youzhixueyuan.com/the-underlying-structure-and-principle-of-hashmap.html HashMap是Java程序员使用频率最 ...
- HashMap的底层原理 cr:csdn:zhangshixi
1. HashMap概述: HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变 ...
- 实在没想到系列——HashMap实现底层细节之keySet,values,entrySet的一个底层实现细节
我在看HashMap源码的时候发现了一个没思考过的问题,在这之前可以说是完全没有思考过这个问题,在一开始对这个点有疑问的时候也没有想到居然有这么个语法细节存在,弄得我百思不得其解,直到自己动手做实验改 ...
- HashMap的底层原理
简单说: 底层原理就是采用数组加链表: 两张图片很清晰地表明存储结构: 既然是线性数组,为什么能随机存取?这里HashMap用了一个小算法,大致是这样实现: // 存储时: int hash = ke ...
- 再学Java 之 HashMap的底层实现
今天参加欢聚时代的面试,我说我自己依靠自己的理解重新实现过HashMap.描述我自己的实现思想后,面试官问“hashmap”底层如果用数组不是效率比较低吗,不是更应该用红黑树吗?我一下子就蒙了.用数组 ...
- 面试题:HashMap和ConcurrentHashMap的区别,HashMap的底层源码。
Hashmap本质是数组加链表.根据key取得hash值,然后计算出数组下标,如果多个key对应到同一个下标,就用链表串起来,新插入的在前面. ConcurrentHashMap:在hashMap的基 ...
随机推荐
- C# MessageBox常用用法
if(MessageBox.Show("message", "title", MessageBoxButtons.OKCancel,MessageBoxIcon ...
- 怎样看懂Oracle的执行计划
怎样看懂Oracle的执行计划 一.什么是执行计划 An explain plan is a representation of the access path that is taken when ...
- 【转】【Raspberry Pi】Unix NetWork Programming:配置unp.h头文件环境
一.初衷 近期正在做网络计算编程的作业.要求平台为unix/linux,想着Raspberry Pi装的Debian系统也是Linux改的,也应该能够勉强用着,所以就用它来做作业了! 二.说明 先把环 ...
- NSDecimalNumber用于精度准确的计算
在处理金额计算时,往往会涉及到小数,由于Double类型不准确,无法做到产品的要求.为了保证金额计算的准确性,建议使用NSDecimalNumber. 1.创建对象(常用的方法) // mantiss ...
- 解决导入so库报错小结
最近公司准备将重构后的项目上线,可是等到我接手的时候发现一个很纠结的问题:安卓5.0以上的手机(例如我现在在用的红米note3)运行重构后项目发生报错,提示缺少某so库. 而5.0以下的手机(我的小米 ...
- hihoCoder 1183 连通性一·割边与割点(Tarjan求割点与割边)
#1183 : 连通性一·割边与割点 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 还记得上次小Hi和小Ho学校被黑客攻击的事情么,那一次攻击最后造成了学校网络数据的丢 ...
- 【转】如何把Json格式字符写进text文件中
http://www.cnblogs.com/insus/p/4306640.html http://json2csharp.chahuo.com/ 本篇一步一步学习怎样把显示于网页的json格式的字 ...
- 基于apache的tomcat负载均衡和集群配置
最近不是很忙,用零碎时间做点小小的实验. 以前公司采用F5负载均衡交换机,F5将请求转发给多台服务器,每台服务器有多个webserver实例,每个webserver分布在多台服务器,交叉式的分布集群. ...
- django对数据查询结果进行排序的方法
在你的 Django 应用中,你或许希望根据某字段的值对检索结果排序,比如说,按字母顺序. 那么,使用 order_by() 这个方法就可以搞定了. ? 1 2 >>> Publis ...
- linux 压缩 解压zip 命令
将当前目录下的所有文件和文件夹全部压缩成test.zip文件,-r表示递归压缩子目录下所有文件zip -r test.zip ./* 打包目录zip test2.zip test2/*解压test.z ...