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. 深海中的STL—mt19937

    mt19937 当你第一眼看到这玩意儿的时候 肯定禁不住吐槽:纳尼?这是什么鬼? 确实,这个东西鲜为人知,但是它却有着卓越的性能 简介 mt19937是c++11中加入的新特性 它是一种随机数算法,用 ...

  2. 精简你的 docker 镜像

    精简你的 docker 镜像 Intro 现在 docker 的使用越来越普遍,今天来谈一下如何精简你的 docker 镜像 为什么要精简 docker 首先来说说为什么要精简 docker 镜像,减 ...

  3. Spark读Hbase优化 --手动划分region提高并行数

    一. Hbase的region 我们先简单介绍下Hbase的架构和Hbase的region: 从物理集群的角度看,Hbase集群中,由一个Hmaster管理多个HRegionServer,其中每个HR ...

  4. 定时删除所有文件夹下的_desktop.ini文件

    写个批处理,删除对应的文件,命名为DELDesktopIni.bat,存于D盘根目录 @echo off :delini for %%a in ( C: D: E: ) DO ( del /f/s/a ...

  5. 实现MongoDB读写分离的“读偏好”介绍

    在某些情况下,将读请求发送给副本集的备份节点是合理的,例如,单个服务器无法处理应用的读压力,就可以把查询请求路由到可复制集中的多台服务器上.现在绝大部分MongoDB驱动支持读偏好设置(read pr ...

  6. 扩展1000!(n!)的尾数零的个数

    #include <stdio.h> #include <malloc.h> //计算1000!尾数零的个数 //扩展n!的尾数零的个数 //2^a * 5^b //obvio ...

  7. 【公众号系列】浅谈SAP项目管理的技能

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[[公众号系列]浅谈SAP项目管理的技能   写 ...

  8. Left Jion和where区别

    首先,新建两张表A和B,然后插入6条数据到A表,3条数据到B表.语句如下: create table A( ID ,) not null, AName ) null ) create table B( ...

  9. SQLServer之修改触发器

    修改触发器规则 修改CREATE TRIGGER语句以前创建的 DML.DDL 或登录触发器的定义.触发器是通过使用CREATE TRIGGER创建的.这些触发器可以由Transact-SQL语句直接 ...

  10. LVS+Keepalived实现mysql的负载均衡

    1 初识LVS:Linux Virtual Server 1.1 LVS是什么       LVS是Linux Virtual Server的简称,也就是Linux虚拟服务器, 是一个由章文嵩博士发起 ...