List , Set继承至Collection接口,Map为独立接口
Set下有HashSet,LinkedHashSet,TreeSet
List下有ArrayList,Vector,LinkedList
Map下有Hashtable,LinkedHashMap,HashMap,TreeMap
ArrayList 、LinkedList和Vector
1.线程安全:ArrayList 和LinkedList线程都不安全,而Vector是线程安全的,但是性能远不及前两者;
2.数据结构:ArrayList使用数组,而LinkedList使用双向链表(JDK1.6前为循环链表,JDK1.7取消了循环);
3.时间复杂度:ArrayList:查询get(),因为有数组下标,时间复杂度为O(1);执行add(e)方法时,默认会将元素e追加到末尾,时间复杂度为O(1);指定位置做增删的话(add(i,e)/remove()),数组中第i元素和第i元素之后的(n-i)个元素都要向前或向后移动一位,时间复杂度为O(n-i);
LinkedList:双向链表,add(e)不受元素位置影响,时间复杂度为O(1);指定位置操作(add(i,e)),get()遍历后指针需要移动到指定位置再插入,时间复杂度近似为O(n);删除remove(),指针直接指向操作,时间复杂度O(1)。
4.使用场景:对于需要频繁查询的数据,使用ArrayList;频繁增删和更新,使用LinkedList;
HashMap 和 HashTable
1.线程安全:HashMap 是⾮线程安全的,HashTable 是线程安全的,但效率低下且基本被淘汰;
2.初始化容量和扩容:HashMap 默认的初始化⼤⼩为16。之后每次扩充,容量变为原来的2倍,Hashtable 默认的初始⼤⼩为11,之后每次扩充,容量变为原来的2n+1;创建时如果给定了容量初始值,那么 Hashtable 会直接使⽤你给定的⼤⼩,⽽ HashMap 会将其扩充为2的幂次⽅⼤⼩,也就是说 HashMap 总是使⽤2的幂作为哈希表的⼤⼩;
3.数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了很⼤的变化,当链表⻓度⼤于阈值(默认为8)时,将链表转化为红⿊树,以减少搜索时间。Hashtable 没有这样的机制。
HashMap 和 HashSet
HashMap储存key-value键值对,使用key计算hashcode;
HashSet储存不重复的对象,使用对象来计算hashcode。
HashSet检查重复原理
当你把对象加⼊ HashSet 时,HashSet会先计算对象的 hashcode 值来判断对象加⼊的位置,同时也会与其他加⼊的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调⽤ equals() ⽅法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让加⼊操作成功。(《Head fist java》第⼆版)。
HashMap的底层实现
JDK1.8 之前 HashMap 底层是 数组和链表 。HashMap 通过 key 的hashCode 经过扰动函数(HashMap 的 hash 方法,减少碰撞冲突) 处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这⾥的 n 指的是数组的⻓度),如果当前位置存在元素的话,就判断该元素与要存⼊的元素的 hash值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法(创建⼀个链表数组,数组中每⼀格就是⼀个链表。若遇到哈希冲突,则将冲突的值加到链表中即可) 解决冲突。
JDK1.8之后,HashMap是数组+链表/红黑树,解决哈希冲突时且当链表⻓度⼤于阈值(默认为8)时,将链表转化为红⿊树,以减少搜索时间。