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的底层实现的更多相关文章

  1. 深入解析Java对象的hashCode和hashCode在HashMap的底层数据结构的应用

    转自:http://kakajw.iteye.com/blog/935226 一.java对象的比较 等号(==): 对比对象实例的内存地址(也即对象实例的ID),来判断是否是同一对象实例:又可以说是 ...

  2. HashMap的底层实现原理

    HashMap的底层实现原理1,属性static final int MAX_CAPACITY = 1 << 30;//1073741824(十进制)0100000000000000000 ...

  3. HashMap的底层原理(jdk1.7.0_79)

    前言 在Java中我们最常用的集合类毫无疑问就是Map,其中HashMap作为Map最重要的实现类在我们代码中出现的评率也是很高的. 我们对HashMap最常用的操作就是put和get了,那么你知道它 ...

  4. HashMap的底层结构和原理

    http://youzhixueyuan.com/the-underlying-structure-and-principle-of-hashmap.html HashMap是Java程序员使用频率最 ...

  5. HashMap的底层原理 cr:csdn:zhangshixi

    1.    HashMap概述: HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变 ...

  6. 实在没想到系列——HashMap实现底层细节之keySet,values,entrySet的一个底层实现细节

    我在看HashMap源码的时候发现了一个没思考过的问题,在这之前可以说是完全没有思考过这个问题,在一开始对这个点有疑问的时候也没有想到居然有这么个语法细节存在,弄得我百思不得其解,直到自己动手做实验改 ...

  7. HashMap的底层原理

    简单说: 底层原理就是采用数组加链表: 两张图片很清晰地表明存储结构: 既然是线性数组,为什么能随机存取?这里HashMap用了一个小算法,大致是这样实现: // 存储时: int hash = ke ...

  8. 再学Java 之 HashMap的底层实现

    今天参加欢聚时代的面试,我说我自己依靠自己的理解重新实现过HashMap.描述我自己的实现思想后,面试官问“hashmap”底层如果用数组不是效率比较低吗,不是更应该用红黑树吗?我一下子就蒙了.用数组 ...

  9. 面试题:HashMap和ConcurrentHashMap的区别,HashMap的底层源码。

    Hashmap本质是数组加链表.根据key取得hash值,然后计算出数组下标,如果多个key对应到同一个下标,就用链表串起来,新插入的在前面. ConcurrentHashMap:在hashMap的基 ...

随机推荐

  1. 【BZOJ】3505: [Cqoi2014]数三角形

    题意 \(n * m(1 \le n, m \le 1000)\)的网格,求顶点在格点上三角形的个数. 分析 假设\(n \le m\) \(ans = \binom{(n+1) * (m+1)}{3 ...

  2. 使用TFHelp解析Html

    似乎是第一次使用TFHelp解析Html,也是第一次解析Html遇到挺多的难题,现在这里简单的总结一下,慢慢补充TFHelp的使用! https://github.com/topfunky/hpple ...

  3. js获取浏览器前缀

    <script> var div = null; var _prefix = (function (temp) { var prefix = ["webkit", &q ...

  4. Lua字符串库

    1. 基础字符串函数:    字符串库中有一些函数非常简单,如:    1). string.len(s) 返回字符串s的长度:    2). string.rep(s,n) 返回字符串s重复n次的结 ...

  5. EMF学习,为了实现可扩展可自定义的模型验证 - emf quary 与 OCL

    http://blog.csdn.net/javaman_chen/article/details/6020050 //这里的验证强调的是condition的构造,基于查找来判断验证结果.支持OCL也 ...

  6. 关于Apache/Tomcat/JBOSS/Neginx/lighttpd/Jetty等一些常见服务器的区别比较和理解

    先说Apache和Tomcat的区别: Apache是世界使用排名第一的Web服务器软件.它可以运行在几乎所有广泛使用的计算机平台上,由于其跨平台和安全性被广泛使用,是最流行的Web服务器端软件之一. ...

  7. python学习道路(day8note)(抽象类,类的方法,异常处理,socket编程)

    1.#面向对象 #抽象接口 === 抽象类 #就是架构师给你一个架子,你们去写,如果满足不了直接报错 #python2 print("python2---抽象类".center(2 ...

  8. lvs源代码分析

    以linux-2.6.21为例. 数据结构介绍: ip_vs_conn 对于某个连接记录其N元组, (client, vserver, rserver) & (address, port) Q ...

  9. How to use groovy script on jenkins

    1. Install groovy plugin 2. Add a step of groovy. (normal & systerm) 3. Execute groovy script im ...

  10. php数字索引数组去重及恢复索引

    $tmp = array('a','b','c','a'); $tmp = array_values(array_unique($tmp)); print_r($tmp);exit; //输出 Arr ...