目录
一、Map
Map是一种键值对集合,Map集合中的每一个元素都包含一个键对象和一个值对象。其中,键对象不允许重复,而值对象可以重复,并且值对象还可以是Map类型的,就像数组中的元素还可以是数组一样。
二、实现类一:HashMap
一、底层实现
Map<K,V>是一种键-值映射表,当我们调用put(K key,V value)方法时,就把key和value做了映射并放入Map。当我们调用V get(K key)时,就可以通过key获取到对应的value。如果key不存在,则返回null。和list类似,Map也是一个接口,最常用的实现类就是HashMap。
HashMap内部数据结构使用数组+链表+红黑树进行存储。数组类型为Node[],每个Node都保存了某个键值对元素的key、value、hash、next等值。由于next的存在,所以每个Node对象都是一个单项链表中的组成节点。
当新添加一个KV键值对元素时,通过该元素的key的hash值,计算该元素在数组中应该保存的位置。如果该下标位置如果已经存在其他Node对象(产生hash冲突),则采用连地址法处理,即将新添加的KV键值对元素将以链表形式存储。将新元素封装成一个新的Node对象,插入到该下标位置的链表尾部。当链表的长度超过8并且数组长度大于64时,为了避免查找搜索性能下降,该链表会转换成一个红黑树。
二、HashMap的扩容
HashMap初始化时,默认的数组大小只有16,通过位运算1<<4计算得出。并且,数组的长度必须为2的幂次方。因为数组长度为2的幂次方,同时可以使用&运算,结合hash值,快速计算该元素在数组中的下标位置,提高HashMap的使用效率。
扩容过程中是需要按照数组容量和加载因子来进行判断的。
数组容量:基础数组Node<K,V>[] table 的长度。如果没有指定容量,添加第一个元素时,该数组按照默认值16进行初始化。
加载因子:用来表示HashMap集合中的元素的填满程度,默认为0.75f。越大则表示可以填充的元素越多,集合的空间利用率越高,但是冲突的机会就会增大。
扩容有两种情况:
- HasnMap中的元素超过扩容阈值threshold时,就会进行数组扩容(扩容阈值=数组容量*加载因子)。
- HashMap加入新元素时,如果链表的长度大于8,会将当前链表装换为红黑树。在转换为红黑树之前时,会判断数组长度,如果小于64,会产生数组扩容。如果数组长度大于64,才会将链表转换为红黑树。
扩容多少:HashMap每次扩容时,会按照当前数组的容量扩容2倍。
最大容量:每次HashMap进行扩容时,都会检查当前容量是否超过常量值MAXUMUM_CAPACITY,如果超出,则不扩容。常量值通过位运算1<<30,计算结果为107371824。
三、与HashTable的区别
HashTable是继承自Dictory抽象类。
在没有指定内存大小的时候 ,默认的数组大小是11,扩容是2n+1;
1.底层结构:HashMap和Hashtable的底层结构是都是数组加链表;
2.安全性:HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过synchronized 修饰;
3.对null键和null值的支持:HashMap支持null键和null值,可以有多个null值但只能有一个null键。而HashTable不支持,只要键和值有一个为null就会抛出NullPointerException;
4.效率:因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它;
四、LinkedHashMap
HashMap和双向链表(是用于保持迭代顺序)合二为一既是LinkedHashMap。所谓LinkedHasnMap,其实是一个将所有Entry节点链入一个双向链表的HashMap。它是HashMap的子类,所以LinkedHashMap有HashMap的所有特性。
迭代顺序可以是插入顺序,也可以是访问顺序。因此,根据链表中元素的顺序可以将LinkedHashMap分为:保持插入顺序的LinkedHashMap 和 保持访问顺序的LinkedHashMap,其中LinkedHashMap的默认实现是按插入顺序排序的。
除去红色双向的虚线,就是一个HashMap。
三、实现类二:TreeMap
TreeMap是SortedMap(父接口为Map)接口的实现类。
使用TreeMap时,放入的Key必须实现Comparable接口。Value没有作要求。
如果作为Key的class没有实现Comparable接口,那么,必须在创建TreeMap时同时指定一个自定义比较器。其默认排序时按照自然排序,对于Integer时按照升序,字符按照字母顺序....
TreeMap不需要用hash来定位元素位置,因为没有使用数组来存储元素。