Map:双列数据,存储键值对的数据(实体)
1.HashMap:作为Map的主要实现类;线程不安全、效率高;可存储null的key和value。底层在jdk7及以前是数组加链表;jdk8及以后是数组加链表加红黑树。
2.LinkedHashMap:HashMap的子类;保证遍历map元素时,可以按照添加顺序遍历。因为在原有HashMap底层结构基础上,添加了一对指针,指向前一个数据和后一个数据。适用于频繁的遍历操作。
3.TreeMap:保证按照添加的键值对进行排序,实现排序遍历。排序原则:对键进行自然排序或定制排序。底层使用红黑树。
4.HashTable:作为古老的实现类;线程安全、效率低;不能存储null的key和value。
5.Properties:HashTable的子类;常用来处理配置文件。键和值都是String类型的。
6.HashMap底层实现原理:以jdk7为例说明:调用空参构造器时创建默认长度为16的Entry类型数组。添加键值对key1-value1时:调用key1所在类的hashcode方法求出key1的哈希值,根据此哈希值通过某种算法计算出该键值对在数组中的索引位置。若此位置无数据添加成功。若此位置存在一个数据或以链表形式存在多个数据,则比较key1和已存在的一个或多个数据的哈希值,若都不相同则添加成功。若key1和已存在的某个数据如key2-value2的哈希值相同,则调用key1所在类的equals(key2)方法。若返回值为false则添加成功。若返回值为true则修改相同键的值为最新要添加的值(使用value1替换原有值value2)。在不断的添加过程中,会涉及到扩容问题,当哈希表长度大于扩容临界值且要插入的索引位置为非空时(哈希碰撞),默认的扩容方式为:将数组容量扩大为原来的2倍,并将原有的数据复制过来,复制过程中会重新计算每个数据在数组中的索引位置。
7.为什么扩容条件为大于临界值而不是存满?
因为HashMap的数据结构有可能不会存满,而是不断的加长链表。为了尽可能的减小链表的长度,把扩容条件设为大于临界值而不是存满。
8.源码中的知识点:DEFAULT_INITIAL_CAPACITY:默认数组容量为16;当调用带参构造器创建HashMap对象时,若指定的容量不是2的整数次幂,创建的数组容量为大于该参数的最小2的整数次幂;DEFAULT_LOAD_FACTOR:HashMap的默认加载因子为0.75;threshold:扩容的临界值为数组当前容量乘加载因子;TREEIFY_THRESHOLD:Bucket(数组某一索引位置上)中的链表长度大于该默认值8,且哈希表长度大于64时,转化为红黑树;MIN_TREEIFY_CAPACITY:桶(Bucket)中的Node被树化时最小的Hash表容量,默认为64,若桶中链表长度大于TREEIFY_THRESHOLD但哈希表长度不大于MIN_TREEIFY_CAPACITY则执行resize()进行扩容。
9.Collection与Collections的区别:Collection是存储单列数据的集合的接口,子接口有List和Set。Collections是操作Collection的工具类,所有方法均为静态方法。
jdk8相较于jdk7在底层实现的不同:
1.调用空参构造器时底层没有创建长度为16的数组。
2.jdk8底层数组为Node[]而非Entry[],仅仅类型名称改变其结构没变。
3.首次调用put()方法时,底层创建长度为16的数组。
4.jdk7底层数据结构为:数组+链表。jdk8底层数据结构为:数组+链表+红黑树。当数组的某一个索引位置上的元素以链表的形式存在的数据个数>8且当前数组的长度>64时,此时此索引位置上的所有数据改为使用红黑树存储。因为当数组某一索引位置上的数据过多时,在链表中查找指定数据需要逐个遍历,效率低。改用红黑树存储,每次查找都是二分查找,效率高。