理解HashMap的原理

HashMap的底层实现是一个数组结构,数组中的每一项又是一个Map.Entry<K,V>,这个键值对持有下一个元素的引用,这就构成了链表结构,在初始化的时候会去初始化这个数组。

从上面这段话本人的理解即是HashMap宏观上为数组结构,微观上为链表结构,将两者结合取其优点的一种数据结构。

Java基本结构:一个是数组,另外一个是链表结构

数组特性:

寻址容易,插入和删除困难

  • 确定其下标情况下查询的时间复杂度为O(1),因其为顺序存储,直接定位get即可。
  • 不确定下标的情况下查询的时间复杂度为O(N),因需要进行循环对比得出其值。
  • 插入的时间复杂度为O(N),需要循环将数组的位置一个一个移动。

链表特性:

寻址困难,插入和删除容易。

  • get时需循环链表进行对比,时间复杂度O(N)。因链表在内存中的存储是非连续的。
  • 插入或删除的时候时间复杂度为O(1),只需修改链表的引用即可。

HashMap实现原理分析

put操作时的主要是根据键(Key)来确定存储位置,其大致流程为:Key.hashCode() ——> hash() ——> indexFor() ——> 存储下标。

  • key.hashCode()为返回对象的哈希码值。
  • hash()为一个经典的散列算法。
  • indexFor()通过此函数对数组的长度取模运算,保证得出的存储位置在数组下标内。
  • 在进行addEntry()操作的过程中会自动对map进行扩容操作,扩容条件为发生哈希冲突且Entry.length>数组大小(默认2^4)*临界阈值(0.75)的时候。
  • 发生哈希冲突时通过equals比较Entry.key,返回true时,仅覆盖原有Entry.key的value值,返回false时,新添加的Enter与原Entry形成Entry链,且总是位于Entry的头部

get()操作同理,根据键(key)确定其数组的存储下标,根据下标确定其Entry链表中某Entry对象,返回Entry.value值。

  • key为null时获取数组位置0上的Entry。
  • 获取key.hashCode——>hash()——>indexFor()——>取得数组下标indexOf。
  • 根据数组特性获取数组下标值为indexOf的Entry链表。
  • 循环Entry链表,比较其hash(key.hashCode)值且利用equals或==方法比较其key,返回其Entry.value。

链地址法解决Hash冲突。基本思想:为每个Hash值建立一个链表,当发生冲突时,将记录插入到链表中的头部

终于通过大牛们的讲解和自己的理解大致体会了其HashMap的实现原理,精髓所在。