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. Apple Pay--iOS开发

    一.什么是Apple Pay? 1. 概念 Apple Pay,简单来说, 就是一种移动支付方式.通过Touch ID/ Passcode,用户可使用存储在iPhone 6, 6p等设备上的信用卡和借 ...

  2. YUM源设置

    1挂载光盘 先创建一个文件 /aaa 然后挂载mount /dev/cdrom /aaa 进入 /aaa   ls 查看是否挂载OK 2进入yum文件夹.将除Media以外的所有文件名改为XXXXXX ...

  3. PHP初学习笔记(2015/4/8)

    //echo C('name'); App/Action/IndexAction.class.php文件夹下的 URL模式 //输出URL模式 //echo C('URL_MODEL'),'<b ...

  4. android获取本地图片并显示图片

    import java.io.FileNotFoundException; import android.content.ContentResolver; import android.content ...

  5. Service之三种服务方式

    (一)StartService 运行Service的方法之一.任何继承于android.content.Context的Android组件(component)都可以使用一个Intent(androi ...

  6. SQL SERVER数据类型与C#数据类型对照表

      SQL SERVER类型   C#类型 精确数字 bigint 从 -2^63 (-9223372036854775808) 到 2^63-1 (9223372036854775807) 的整型数 ...

  7. bootstrap css总结

    base css 我分为了几大类 1,列表 .unstyled(无样式列表),.dl-horizontal(dl列表水平排列) 2,代码 code(行级),pre(块级),.pre-scrollabl ...

  8. Python强化训练笔记(六)——让字典保持有序性

    python的字典是一个非常方便的数据结构,使用它我们可以轻易的根据姓名(键)来找到他的成绩,排名等(值),而不用去遍历整个数据集. 例如:{'Lee': [1, 100], 'Jane': [2, ...

  9. java并发编程-基础

    线程带来的风险 安全性:多线程操作执行顺序的不可预测性 -- 永远不发生糟糕的事情: 活跃性:代码无法得到执行,死锁.饥饿问题 -- 某件正确的事情最终会发生: 性能问题:活跃性只意味着某件事最终会发 ...

  10. Nodejs:Glob对象

    模块Glob: glob主要用处为筛选文件 API样例: var globInstance = new glob.Glob("@(a|a1|b).js",{nonull:true, ...