List
ArrayList:jdk1.2
- 使用 Ojbect[] 数组来存储数据,list当前大小通过变量size来记录
- 扩容系数为1.5倍
- 默认容量为10,使用无参构造函数初始化,内部数组指向DEFAULTCAPACITY_EMPTY_ELEMENTDATA,扩容时为15
- 使用带参构造函数初始化指定容量为0,内部数组指向EMPTY_ELEMENTDATA,扩容时为1
- 增删时都调用System.arraycopy函数
- 指定初始容量初始化时,会初始化Object[]数组,但size仍为0,所以size()函数仍返回0,且set()函数会抛异常,必须进行add操作size才会变化
Verctor:jdk1.0
- 使用Object[]数组来存储数据,elementCount记录当前大小
- 初始化时可指定扩容增长的固定值capacityIncrement,若固定值为0,则在扩容时,固定值动态为扩容前数据的大小,即认定扩容系数为2倍
- 默认容量10
- 内部所有关键方法都加了synchronized
- 可以保证关键方法单独调用是线程安全,但若存在多个线程同时组合调用同一个方法,仍可能不安全,比如删除最后一个元素remove(size() - 1),可能发生下标越界
LinkedList:jdk1.2
- 同时实现了List、Deque接口,所以可以拿来当成【数组、队列、栈】使用
- 底层为双向链表结构,保存链表的头、尾节点,first、last指针,通过size变量记录长度
Map
HashMap:jdk1.2
- jdk1.8以前底层结构为数组+链表,jdk1.8及其以后为数组+链表+红黑树
- jdk1.8,数组为Node数组,链表与红黑树共用Node数据结构
- Node数组大小永远为2的N次方,默认大小为16,扩容的负载因子默认0.75
- 哈希冲突的概率符合泊松分布,当负载因子在7~8时,冲突概率极低
- 哈希值计算:先散列 hashCode(key) >>> 16 ^ hashCode(key) ,将key的hashcode码的高16位与低16位进行【异或】运算;将散列后的值与【数组长度 - 1】做【与】运算
- 当size达到当前容量与负载因子的乘积后,就扩容两倍,同时将原数组的Node复制到扩容后的数组,因为在旧数组(长度记为n)中同一下标的哈希值低【n - 1】位是相同的,扩容后需计算低【n】位,所以对同一下标的Node采用高位、低位链按第n位的值来划分
- key为null的永远在数组第0个下标上
- 当链表长度为8时,若size小于64,就先进行一次扩容,扩容后将链表转红黑树
- 当红黑树长度小于6时,退化为链表
- jdk1.7链表插入采用头插法,多线程下会造成环,jdk1.8改为尾插法
- jdk1.8不是线程安全的,会出现put覆盖
LinkedHashMap:jdk1.4
- 继承自HashMap,内部同时使用Entry扩展HashMap.Node结构,增加after、before指针信息来维护所有Node的插入顺序,形成一个双向链表
- 内部保留head、tail两个指针,指向双向链表的头尾节点
- 内部增加accessOrder标志位,false代表按插入顺序访问;true代表按最近访问顺序,最近访问的节点越靠近tail,越久没访问的节点约靠近head
- 可用来实现LRU,设置accessOrder为true,并重写removeEldestEntry方法,return size() > capacity
TreeMap:jdk1.2
- 内部维护一个Entry节点,Entry结构包括key、value、left、right、parent、color
- 由此可以看出其底层结构为一颗红黑树,同时与HashMap的区别在于,TreeMap间接实现了SortedMap接口,即key是有序的
HashTable:jdk1.0
- 底层维护数组+链表
- 数组默认长度11,扩容时大小为2倍 + 1,并头插
- 所有对外方法均使用sychronized修饰
- key或value不允许为null
- 哈希冲突比hashmap严重
Set
HashSet:jdk1.2
- 内部存储一个HashMap对象,仅使用map的key来存储元素,value一律应用内部定义的Present对象
- 迭代是无序的
TreeSet:jdk1.2
- 底层默认使用TreeMap来初始化,内部通过NavigableMap来持有初始化时的TreeMap引用
- 与HashSet类型,TreeMap的value一律应用内部定义的Present对象
- 因为NavigavleMap继承了SortedMap,所以是有序的