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. 自定义工作流活动运行产生System.Security.SecurityException

    摘要: 微软动态CRM专家罗勇 ,回复305或者20190224可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . 最近碰到一个 ...

  2. vmware 开启VM的硬件cpu虚拟化功能

    物理机的cpu硬件虚拟化功能,通过开启bios中的设置,而vmware中创建的虚拟机也可以开启该特性,如下. 这样的话,就直接可以基于这些VM安装openstack计算节点.控制节点.网络节点了.

  3. MySQL 常用指令小结

    l  创建数据库:CREATE DATABASE table_name; l  删除数据库:DROP DATABASE table_name; l  展示数据库:SHOW DATABASE; l  选 ...

  4. (最详细)小米MIX的Usb调试模式在哪里打开的教程

    当我们使用安卓手机链接pc的时候,或者使用的有些应用比如我们团队营销团队当使用的应用引号精灵,以前老版本就需要开启Usb调试模式下使用,现当新版本不需要了,如果手机没有开启Usb调试模式,pc则没法成 ...

  5. idea部署Maven入门(一)——环境变量的配置和下载

        介绍:       1  Maven是用来管理jar包的一种工具,       2  Maven主要是构建java项目和java web项目        3 maven项目管理所依赖的jar ...

  6. MySQL的自动提交模式

      默认情况下, MySQL启用自动提交模式(变量autocommit为ON).这意味着, 只要你执行DML操作的语句,MySQL会立即隐式提交事务(Implicit Commit).这个跟SQL S ...

  7. Linux PXE无人值守网络装机

    Linux PXE无人值守网络装机 一.实验环境: 2台Linux系统(RHEL6.5版本),1台作为:PXE远程安装服务器(安装dhcp服务.ftp服务.tftp服务),另1台作为:客户端(未装RH ...

  8. Win10解除网速限制让网速飞起来

    Win10隐藏了20%的网速,下面叫你怎么释放它: 1.按Win+R调出运行,输入gpedit.msc点击确定; 2.点击windows设置,右击基于策略Qos,选择高级Qos设置: 3.勾选如图方框 ...

  9. mysql导出表的字段及相关属性

    需要导出数据库中表的字段及属性,制成表格保存到word中 首先找到要导的库, 在查询页面输入sql SELECT COLUMN_NAME 列名, COLUMN_TYPE 数据类型, DATA_TYPE ...

  10. 合并两个有序链表的golang实现

    将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 输入:->->, ->-> 输出:->->->->-> ...