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. AngularJS处理服务器端返回的JSON数据的格式问题

    用ng的$http服务发起ajax请求,php返回的JSON数据格式要正确! 一开始我的php页面是这样返回数据的: if($result){ $oid = mysqli_insert_id($con ...

  2. 【Dojo 1.x】笔记2 使用服务器环境及使用模块

    又开坑了.上次静态html页面完成本地module的引用,算是成功了,但是并不知道是怎么运作的,没关系慢慢来. 我用的环境是VSCode,这次因为官方说要在服务器环境下运行,所以就用上了VSCode的 ...

  3. 命令行以及Python交互模式下python程序的编写

    一.命令行模式 在Windows开始菜单选择“命令提示符”,就进入到命令行模式,它的提示符类似C:\>: 二.Python交互模式 在命令行模式下敲命令python,就看到类似如下的一堆文本输出 ...

  4. MIUI10系统怎么样刷成开发版获得ROOT权限

    小米的设备不同手机型号正常情况下小米官方论坛都提供两个不同系统,可分为稳定版和开发版,稳定版没有提供root超级权限管理,开发版中就提供了root超级权限,在很多工作的时候我们需要使用的一些功能强大的 ...

  5. C++ 重点关键字

    const 四种用法 1.修饰变量起到限定只读作用: void func(const int a, const string str) {...} const int* func(...) {...} ...

  6. 商家APP店内点餐开启有桌台点餐模式

    商家APP店内点餐开启有桌台点餐模式 步骤一:管理员后台-配置管理--店铺配置--简易付tab页--是否支持扫码下单-是 步骤二:管理员后台-配置管理--设备管理--选择对应的机器--配置--云POS ...

  7. 【原】Java学习笔记021 - Object

    package cn.temptation; public class Sample01 { public static void main(String[] args) { // 类 Object: ...

  8. c/c++ 多线程 等待一次性事件 packaged_task用法

    多线程 等待一次性事件 packaged_task用法 背景:不是很明白,不知道为了解决什么业务场景,感觉std::asynck可以优雅的搞定一切,一次等待性事件,为什么还有个packaged_tas ...

  9. python3 set(集合)

    add(增加元素) name = set(['Tom','Lucy','Ben']) name.add('Juny') print(name) #输出:{'Lucy', 'Juny', 'Ben', ...

  10. laravel项目使用appnode部署linux系统到阿里云服务器流程记录(待补充)

    使用 SSH 连接工具,如 PuTTY.XShell.SecureCRT 等,连接 Linux 服务器后(阿里云服务器命令行内直接输入appnode安装命令,版本:mysql选5.7.php选7.2) ...