Java集合概览
List,Set,Queue和Map的区别
List:存储的元素是有序的、可重复的。
Set:存储的元素是无序的、不可重复的。
Queue:按特定的排队规则来确定先后顺序,存储的元素是有序的、可重复的。
Map:使用键值对(key-value)存储,每个键最多映射到一个值,key是无序的,不可重复的,value是无序的,可重复的。
List
List是一个有序集合,可以包含重复元素。可以通过索引来访问任何元素。
ArrayList,Vector,LinkedList的区别?
ArrayList和Vector的区别
相同点:
数据结构都是数组,底层使用Object[]存储。
都继承了AbstractList抽象类,都实现了List接口。
不同点:
从源码可看出ArrayList初始容量是0,会在第一次add元素时,扩容为10;Vector初始容量是10。
扩容规则不同,ArrayList为原大小的1.5倍,Vector为原大小的2倍。
因为Vector加锁的原因,导致Vector比ArrayList速度慢。
Vector可以指定增长因子,如果该增长因子指定了,那么扩容的时候会每次新的数组大小会在原数组的大小基础上加上增长因子大小。
ArrayList线程不安全,Vector线程安全(加了synchronized)。
ArrayList和LinkedList的区别
相同点:
都是线程不安全的。
不同点:
ArrayList数据结构是数组,LinkedList数据结构是双向链表。
因为数组和链表的差异,所以ArrayList查询较快,增删较慢。LinkedList增删较快,查询较慢。
因为数组和链表的差异,LinkedList的空间花费比ArrayList更大,因为每个元素都要存放直接前驱和直接后继。
Set
Set是一个不能包含重复元素的集合。
HashSet
内部通过HashMap实现,HashMap底层是数组+链表+红黑树。
允许放一个null值。
无序的。
线程不安全。
扩容机制:HashSet内部是HashMap,第一次添加时,table数组扩容到16;
临界值(threshold)= 容量*加载因子(loadFactor)为0.75,即16*0.75=12,如果table数组使用到了临界值12,就会扩容到16*2=32,新的临界值为32*0.75=24。
转化为红黑树:
LinkedHashSet
HashSet子类,内部通过LinkedHashMap来实现,LinkedHashMap的底层是数组+双向链表。
允许放一个null值。
有序的,满足FIFO。
线程不安全。
继承了HashSet,所以是HashSet基础上增加了元素添加顺序功能。
TreeSet
内部通过TreeMap实现,TreeMap底层是红黑树(自平衡的排序二叉树)。
不允许有null值。
有序的,但是不保证是插入顺序,而是默认使用元素自然排序,可以自定义排序器。
线程不安全。
Queue
队列是一种特殊的线性表,单向队列只允许在表的前端进行删除操作,在表的后端进行插入操作。即FIFO,先进先出规则。
双端队列就是对头和队尾都可以进行插入和删除操作。
Queue接口为单向队列。
Deque接口继承了Queue,增加了在队首插入和队尾删除的方法,即为双端队列。同时还提供了push()和pop()等其他方法,用于模拟栈。LinkedList实现了Deque接口,即LinkedList可作为队列和栈使用。
ArrayDeque与LinkedList的区别
相同点:
都是线程不安全。
不同点:
ArrayDeque是基于循环数组实现双端队列,LinkedList通过双向链表实现。
ArrayDeque初始化为16(JDK8),扩容为原来的2倍。
ArrayDeque不支持存储null数据,LinkedList支持。
当ArrayDeque被当作栈使用时比Stack快,当作队列使用时比LinkedList快;
因为Stack继承Vevtor,Vevtor因为线程安全每个方法都加了锁,
而LinkedList是因为数据结构是链表,下标索引访问比指针访问速度快,所以寻址速度不如ArrayDeque。
集合类型 | 数据结构 | 初始化及扩容 | 插入/删除时间复杂度 | 查询时间复杂度 | 线程是否安全 | 能否存储null |
ArrayDeque | 循环数组 | 初始化:16 扩容:2倍 | O(n) | O(1) | 否 | 能 |
LinkedList | 双向链表 | 无 | O(1) | O(n) | 否 | 否 |
PriorityQueue
与Queue区别在于元素出队顺序是与优先级相关。
利用二叉堆的数据结构实现,底层使用可变长的数组来存储数据。
通过堆元素的上浮和下沉,实现了在O(logn)的时间复杂度内插入元素和删除堆顶元素。
线程不安全。
不支持存储null和non-conparable对象。
默认是小顶堆,但可以接受一个Comparator作为构造参数,从而来自定义元素优先级的前后。
Map
存储一组键值对象,提供key(键)到value(值)映射。
HashMap和Hashtable区别
不同点:
(JDK1.8以后)HashMap的底层数据结构是数组+链表+红黑树,Hashtable是数组+链表。
HashMap线程不安全,Hashtable线程安全(加了synchronized)。
HashMap比Hashtable效率高一点,因为Hashtable加锁。
HashMap可以存储null的key和value,但是null作为键只能有一个,null作为值可以有多个;Hashtable不允许有null键和null值。
HashMap默认初始化大小为16,每次扩容,容量变为2n;Hashtable默认初始化大小为11,每次扩容,容量变为2n+1。
ConcurrentHashMap
JDK1.8以后:
ConcurrentHashMap可以理解成线程安全的HashMap。
ConcurrentHashMap线程安全的实现方式是cas操作(乐观锁)+synchronized关键字。
ConcurrentHashMap的key和value均不可以为null。
注:CAS(Compare-And-Swap):比较并交换
CAS就是通过一个原子操作,用预期值去和实际值做对比,如果实际值和预期相同,则做更新操作。
如果预期值和实际不同,我们就认为,其他线程更新了这个值,此时不做更新操作。
而且这整个流程是原子性的,所以只要实际值和预期值相同,就能保证这次更新不会被其他线程影响。
TreeMap
TreeMap和HashMap都继承了AbstractMap,同时TreeMap还实现了NavigableMap接口和SortedMap接口。
实现了NavigableMap接口让TreeMap可以对集合内元素进行搜索。
实现了SortedMap接口让TreeMap可以对集合内的元素根据键排序。默认是按key升序排序,也可以指定排序的比较器。