常用集合的底层数据结构和实现-Map

来源:互联网 发布:心动网络面试题 编辑:程序博客网 时间:2024/06/05 08:22

常用集合的底层数据结构和实现

 常见的底层数据结构:在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的(当然也不能绝对的说,但至少在java中现有的所以集合都是基于这2中数据结构实现的)

 Map

HashMap

数据实现:底层基于模拟指针,也就是数组和链表的结合。底层整体结构是一个数组,数组中的每个元素又是一个链表。当创建一个HashMap的时候就会初始化一个数据,每次添加一个对象(put)的时候就会产生一个链表对象(也就是数组中的一个元素,Object类型)。集合中的每个Entry就是数组中的一个元素,每个Map.Entry就是一个key-value(键值对),它具有由当前元素指向下一个元素的引用,这就构成了链表。

存储原理:当我们在向HsahMapput元素的时候,先根据可以的hashCode重新计算该对象(也就是key)hash值,根据计算的hash值得到这个元素(key)在数组中的位置(即下标),如果数组该位置已经存在其他元素,那么这个位置的元素将会以链表的形式存放,新加入的放在链头,最先加入的放在链尾,如果数组该位置元素不存在,那么就直接将该元素放到此数组中的该位置。源码中做到将自动对象放到指定位置(下标)的方法是addEntry方法,其中里面涉及到了很复杂的hash算法,包括动态扩展数组的长度和指定位置的链表的结构的重新调整(新创建的对象放入到链头,原来的对象一次排列),其中动态扩展数组长度为了优化查询和存储最大化以及优化散列(元素位置尽量的分布均匀些)等都做了对应的处理,这里就不一一做说明了,如果感兴可以直接自己去了解hash算法和map存储相关知识。

 

去重原理:经过hash算法和一系列的算法优化和链表结构的重构以及高位计算,使得不同的key算的得到的index(数组下标)相同的几率很小,那么在数据范围内公布上就比较均匀,当程序师徒建一个key-value放入到HashMap的时候,首先会根据该keyhashCode()返回的值决定将该Entry放在数组的哪个位

置,如果计算出来的值已经存在数组中即两个EntryhashCode相同,那么他们存储在相同的位置,如果两个EntrykeyhashCode通过equals比较放回true,则新添加的对象掩盖掉原有的Entry,如果返false则新添加的Ebtry将与原来的Entry形成链,且新的Entry在链头,之后的依次排列。

 

读取原理:HashMapget元素时,首先计算keyhashCode,找到数组中对应位置的某一元素,然后通过keyequals方法在对应位置的链表中找到需要的元素。HashMap底层将HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry

 

扩容机制(resize或者rehash):几乎所有的集合都是动态扩容的,当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的。所以为了提高查询的效率,就要对HashMap的数组进行扩容,数组扩容这个操作也会出现在ArrayList中,这是一个常用的操作,而在HashMap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize。那么HashMap什么时候进行扩容呢?当HashMap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,这是一个折中的取值。也就是说,默认情况下,数组大小为16,那么当HashMap中元素个数超过16*0.75=12的时候,就把数组的大小扩展为 2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能

 

其他Map集合的区别和简介:

基本所有的map集合的底层数据结构和操作(存储,读取,删除,覆盖)原理都一样,都是基于数组和链表.

 

1 0
原创粉丝点击