HashMap底层实现

HashMap底层数据结构如下图,HashMap由“hash函数+数组+单链表”3个要素构成

通过写一个迷你版的HashMap来深刻理解

MyMap接口,定义一个接口,对外暴露快速存取的方法,并定义了一个内部接口Entry。

publicinterface MyMap<K,V> {

   public V put(K k, V v);

   public V get(K k);

   publicinterface Entry<K, V>{

       public K getKey();

       public V getValue();

   }

}

MyHashMap接口实现

publicclass MyHashMap<K, V> implementsMyMap<K, V> {

/*HashMap的要素之一就是数组,自然在这里,我们要定义数组,数组的初始化大小,还要考虑扩容的阀值*/

   privatestaticintDEFAULT_INITIAL_CAPACITY = 1 << 4;

   privatestaticfinalfloatDEFAULT_LOAD_FACTOR = 0.75f;

   privateintdefaultInitSize = 1 << 2;

   privatedoubledefaultLoadFactor;

   private Entry<K, V>[] table;

   privateintentryUserSize;

/*MyHashMap构造函数使用到了“门面模式”。这里的2个构造方法其实指向的是同一个,但是对外却暴露了2个“门面”!*/

   public MyHashMap() {

       this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);

   }

   public MyHashMap(intdefaultInitsize, doubledefaultLoadFactor) {

       this.defaultInitSize = defaultInitsize;

       this.defaultLoadFactor = defaultLoadFactor;

       table = new Entry[this.defaultInitSize];

   }

/*Put函数,根据Key计算出在Entry[]中的位置index,如果Entry[index]中的元素为null,那么可以放入其中,如果不为空,那么得遍历单链表,key相同时更新value,不相同时形成一个新的Entry“挤压”单链表。

put函数要考虑扩容和冲突问题,对于扩容:HashMap中的Entry的数量(数组以及单链表中的所有Entry)达到阀值,意味着新生成一个Entry数组并重新散列。关于冲突:利用位运算,让hash函数得到的数据散列开来,从而减低了碰撞的概率,当发生冲突时采用单链表解决冲突。*/

@Override

public V put(K k, V v) {

VoldValue = null;

if (entryUserSize > defaultInitSize * defaultLoadFactor) {

reSize();

}

intindex = hash(k) & (defaultInitSize - 1);

if (table[index] == null) {

table[index] = new Entry<K, V>(k, v, null);

entryUserSize++;

}else {

Entry<K,V> entry = table[index];

Entry<K,V> e = entry;

while (e != null) {

if (k == e.getKey() || k.equals(e.getKey())) {

oldValue = e.getValue();

e.value = v;

break;

}

e = e.next;

}

;

table[index] = new Entry<K, V>(k, v, entry);

entryUserSize++;

}

returnoldValue;

}

/*reSize函数,可以看出,对于HashMap而言,如果频繁进行resize操作,是会影响性能的,这个过程中数组变大,原来数组中的entry元素一个个的put到新数组的过程,需要注意的是一些状态变量的改变。*/

privatevoid reSize() {

List<Entry<K,V>> entryList = new ArrayList<Entry<K, V>>();

for (Entry<K, V> entry : table) {

while (entry != null) {

entryList.add(entry);

entry = entry.next;

}

}

if(entryList.size() > 0){

entryUserSize = 0;

defaultInitSize *= 2;

table = new Entry[defaultInitSize];

for(Entry<K,V> entry : entryList){

put(entry.getKey(),entry.getValue());

}

}

}

/*Has函数,要想散列均匀,就得对hash值进行二进制的位运算!*/

privateint hash(K k) {

inth = k.hashCode();

h ^= (h >>> 20) ^ (h >>> 12);

returnh ^ (h >>> 7) ^ (h >>> 4);

}

/*get函数,需要注意在遍历单链表过程中使用==或者equals来判断下即可。*/

@Override

public V get(K k) {

intindex = hash(k) & (defaultInitSize - 1);

Entry<K,V> entry = table[index];

if (entry == null) {

returnnull;

}

do {

if (k == entry.getKey() || k.equals(entry.getKey())) {

returnentry.getValue();

}

entry = entry.next;

}while (entry != null);

returnnull;

}

/*Entry接口实现*/

publicclass Entry<K, V> implements MyMap.Entry<K, V> {

private K key;

private V value;

private Entry<K, V> next;

public Entry() {

}

public Entry(K key, V value, Entry<K, V> next) {

this.key = key;

this.value = value;

this.next = next;

}

@Override

public K getKey() {

returnkey;

}

@Override

public V getValue() {

returnvalue;

}

}

}

单元测试

publicclass MyHashMapTest {

   @Test

   publicvoid test() {

       MyMap<String,String> map = new MyHashMap<String, String>();

       for(inti =0; i < 30; i++)

           map.put("key" + i, "value" + i);

       for(inti =0; i < 30; i++){

           System.out.println("key" + i + ":" + map.get("key" + i));

       }

   }

}

测试结果

key0:value0    key1:value1    key2:value2    key3:value3

key4:value4    key5:value5    key6:value6    key7:value7

key8:value8    key9:value9    key10:value10   key11:value11  

key12:value12   key13:value13   key14:value14   key15:value15  

key16:value16   key17:value17   key18:value18   key19:value19  

key20:value20   key21:value21   key22:value22   key23:value23  

key24:value24   key25:value25   key26:value26   key27:value27  

key28:value28   key29:value29

HashMap底层实现原理的更多相关文章

  1. HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别(转)

    HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别 文章来源:http://www.cnblogs.com/beatIteWeNerverGiveU ...

  2. Java面试必问之Hashmap底层实现原理(JDK1.7)

    1. 前言 Hashmap可以说是Java面试必问的,一般的面试题会问: Hashmap有哪些特性? Hashmap底层实现原理(get\put\resize) Hashmap怎么解决hash冲突? ...

  3. Java中HashMap底层实现原理(JDK1.8)源码分析

    这几天学习了HashMap的底层实现,但是发现好几个版本的,代码不一,而且看了Android包的HashMap和JDK中的HashMap的也不是一样,原来他们没有指定JDK版本,很多文章都是旧版本JD ...

  4. HashMap底层实现原理(JDK1.8)源码分析

    ref:https://blog.csdn.net/tuke_tuke/article/details/51588156 http://www.cnblogs.com/xiaolovewei/p/79 ...

  5. HashMap底层实现原理及面试常见问题

    HashMap底层源码分析 1.HashMap底层采用的存储结构 1.在JDK1.7及之前采用的存储结构是数组+链表 2.到了JDK1.8之后采用的是数组+链表+红黑树 2.HashMap实现的原理 ...

  6. HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别

    ①HashMap的工作原理 HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象.当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算h ...

  7. (转)HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别

    ①HashMap的工作原理 HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象.当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算h ...

  8. HashMap底层实现原理以及HashMap与HashTable区别以及HashMap与HashSet区别

    ①HashMap的工作原理 HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象.当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算h ...

  9. HashMap底层实现原理及扩容机制

    HashMap的数据结构:数组+链表+红黑树:Java7中的HashMap只由数组+链表构成:Java8引入了红黑树,提高了HashMap的性能:借鉴一张图来说明,原文:https://www.jia ...

随机推荐

  1. splay详解(三)

    前言 上一节我们学习了splay所能解决的基本问题,这节我来讲一下splay怎么搞区间问题 实现 splay搞区间问题非常简单,比如我们要在区间$l,r$上搞事情,那么我们首先把$l$的前驱旋转到根节 ...

  2. 免费开源ERP-成功案例分析(2)

    Odoo用户案例 Odoo用户概要 关于Odoo全球的用户,我们来看一些数据: Odoo目前全球有300万使用者 Odoo系统上每天新创建的数据库超过1000个 Odoo和Word.Excel.Pow ...

  3. iOS ----------各种判断

    iOS 判断数字 - (BOOL) deptNumInputShouldNumber:(NSString *)str { if (str.length == 0) { return NO; } NSS ...

  4. AndroidStudio开发Java工程(解决java控制台中文打印乱码+导入jar包运行工程)

    这篇分享一点个人AS开发java工程经验,虽然有时候还是得打开eclipse来运行java项目,但能用AS的时候还是尽量用AS,毕竟一个字,爽~ 废话不多说,进入正题. 一.开发Java工程 你有两种 ...

  5. C#比较两个由基本数据类型构成的object类型

    /// <summary> /// 比较查询条件 /// </summary> public class ModelExtensions { /// <summary&g ...

  6. 20181112-PostgreSQL数据库dmp文件导入(记录一次数据导入)

    20181112-PostgreSQL数据库dmp文件导入 标注:dmp文件导入,场景:多个schema导入 1. 环境准备: postgres集群master节点上,postgres用户执行以下操作 ...

  7. LeetCode算法题-N-ary Tree Preorder Traversal(Java实现)

    这是悦乐书的第268次更新,第282篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第135题(顺位题号是589).给定一个n-ary树,返回其节点值的前序遍历.例如,给定 ...

  8. e297: write error in swap file

    磁盘空间不足: [root@ipservice fountain]# df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/dock ...

  9. centos7 搭建ntp时钟服务器

    服务器 : 192.168.137.3 客户机:  192.168.137.6 1. 服务器端 centos7下首先确认服务器的防火墙.selinux关闭状态 # cat /etc/redhat-re ...

  10. JavaScript面向对象—基本数据类型和引用数据类型的区别和变量及作用域(函数和变量)

    基本类型和引用类型的值 ECMAScript 变量可能包含两种不同的数据类型的值:基本类型值和引用类型值. 基本类型值指的是那些保存在栈内存中的简单数据段,即这种值完全保存在内存中的一个位置. 而引用 ...