Java之集合的实现细节(一)

来源:互联网 发布:每日行程安排软件 编辑:程序博客网 时间:2024/05/21 10:37
Set和Map
通过继承关系体系,我们可以得出
Set<->Map
EnumSet<->EnumMap
SortedSet<->SortedMap
TreeSet<->TreeMap
NavigableSet<->NavigableMap
HashSet<->HashMap
LinkedHashSet<->LinkeHashMap
表面上这两种集合没有太大关系,但是如果只考虑Map集合的key,不难发现,这些Map集合的key具有一个特征:所有key不能重复,无顺序。将Map集合所有的key集中起来,就是一个set集合。Map集合提供了一个方法来返回所有key组成的Set集合。
Set<K>keySet();
对于一个Map集合而言,其实是一个关联数组.Map总是可以根据该key快速查询对应的value,那么Map集合在保存key-value时可以只考虑key即可。
只要对传统的set稍微做改造,就可以将set改造成map集合,这个map集合功能几乎可以与系统提供的map媲美。

HashMap和HashSet
HashSet和HashMap之间有很多相似之处,对于HashSet而言,系统采用Hash算法决定集合元素的存储位置,可以保证快速存、取集合元素。对于HashMap而言,系统将value当成key的附属,系统根据Hash算法来决定key的存储位置,这样可以保证快速存、取集合key,而value总是紧跟随key存储。
虽然集合号称存储是Java对象,但实际上并不会真正将Java对象放Set集合中,而只是Set集合中保留这些对象的引用而言,Java集合实际上是多个引用变量所组成的集合,这些引用变量指向实际的Java对象。
引用类型的数组一样,当把Java对象放入数组之时,并不是真正把Java对象放入数组中,而只是把对象的引用放入数组中,每个数组元素都是一个引用变量。
当hashmap的每个bucket里存储的Entry只是单个Entry,既没有通过指针产生Entry链时,此时的HashMap具有最好的性能。
当程序通过key取出对应的value时,系统只要计算该key的hashcode()值,再根据该hashcode值找出该key在table中数组中的索引,然后取出该索引出的Entry,最后返回该key对应的value值。在发生"Hash冲突"的情况下,单个bucket存在的不是一个entry值而是entry链,系统必须按顺序遍历每个Entry链,直到找到想搜索的Entry为止。如果恰好要搜索的entry位于该entry链的最末端,必须循环到最好才能找到该元素。

HashMap在底层将key-value当成一个整体进行处理,这个整体就是一个Entry对象。HashMap底层采用一个Entry[]数组来保存所有的key-value对,当需要存储一个Entry对象时,会根据Hash算法来决定其存储位置。当需要取出一个Entry时,也会根据Hash算法找到其存储位置,直接取出该Entry。
HashMap之所以能快速存、取它所包含的Entry时,完全类似于现实生活中的:不同东西要放在不同位置,需要时才能快速找到它。

当创建HashMap时,有一个默认的负载因子,其默认值为0.75。这是时间和空间成本的一种折衷:增大负载因子可以减少Hash表所占用的内存,但是会增加查询数据的时间开销,而查询最频繁的操作(put和get方法都需要用到查询);减少负载因子的话会提高数据查询性能,但会降低Hash表所占用的内存空间。

在创建HashMap时根据实际需要调整load factor的值。如果程序比较关心空间开销,内存比较紧张的话,可以适当增加负载因子。如果程序比较关心时间开销,内存比较宽裕,则可以减少负载因子。通常情况下,不需要改变负载因子。

如果开始就知道hashMap会保存多个key-value值,可以在创建时就使用较大的初始化容量,如果HashMap中的Entry的数量一直不会超过极限容量,HashMap就无需调用resize()方法重新分配table组,从而保证较好的性能。当然,开始就将初始容量设置太高可能会浪费空间。因此创建HashMap时初始化容量设置也需要小心对待。

0 0
原创粉丝点击