在编程语言中,我们必然少不了存储数据的容器,虽然我们有数组,但是数组是连续的开辟处一块连续的内存空间,如果数据过大会无法存储完,数据量小,会浪费空间,所以我们需要使用集合存储数据,而在Java中list和set是我们最常用的集合,它们都属于Collection接口的子接口。
一、Collection 接口(单列集合)
1.子接口
1.1 List接口
1.1.1 特点
List的特点是有序,允许重复(值不唯一)
1.1.2 常见实现类
(1).线程不安全
ArrayList
1.数据结构:数组:Object[ ] elementData
源码:
2.使用场景:查找、遍历效率高,插入、删除效率低
3.扩容方式:
集合相比数组的优势之一就是其内存储空间可变化,那么它是如何变化的,而且它的数据结构是一个Object数组,但数组并没有完成初始化,又在什么时候完成初始化。且容量不足时又如何扩容呢。它们都与其内部定义的成员变量(默认数组),以及无参,有参构造方法,add()方法有关,下面我们先观察源码
内部定义的成员变量与数组:
无参构造方法源码:
有参构造方法源码:
add()方法:
(1).初始化:
- 无参构造方法,数组的初始化容量为0,添加第一个元素时,数组容量扩容为10
- 有参构造方法,数组按指定容量初始化
(2).容量不足时: 按照数组现有容量的1.5倍扩容
LinkedList
1.数据结构:链表:双向链表
源码:
2.使用场景:插入、删除效率高,查找效率低
3.扩容方式:链表动态扩容,每添加一个节点,链表动态扩容一个元素空间
观察源码可知,当我们调用add方法添加元素时,其就会动态的添加一个节点,链表动态的扩容,并且如果是add()方法,或者 addLast()方法添加到链表尾部(尾插法),如果是addFirst()方法就是动态添加到头部(头插法)。
此类为我们提供了不同参数的add方法,也可根据参数,动态的将元素添加到任意位置
(2).线程安全
Vector
1.数据结构:数组:Object[ ] elementData
源码:
2.扩容方式
(1).初始化:
- 无参构造方法,数组的初始化容量为10
- 有参构造方法,数组按指定容量初始化
(2).容量不足时:按照数组现有容量的2倍或指定容量值(capacityIncrement)进行扩容
3.线程安全:通过“synchronized”同步锁实现
Stack
1.stack是vector的子类,数据结构与扩容方式与vector相同
2.特点:先进后出FILO or 后进先出LIFO
CopyOnWriteArrayList
1.数据结构:数组:Object[] array
源码:
2.线程安全:
- 通过“ReentrantLock”锁实现
- CopyOnWrite:"写入"操作时,先进行数组复制,然后在新数组中进行写入操作,然后替换;允许读写同时进行;
1.2 Set接口
1.2.1 特点
set的特点是无序,不允许重复(值唯一)
1.2.2 常见实现类
HashSet
1.特点:无序
2.数据结构:HashMap
LinkedHashSet
1.特点:有序
2.数据结构:LinkedHashMap
TreeSet
1.特点:自动排序
2.数据结构:TreeMap
1.3 Queue接口
1.3.1特点
Queue的特点是先进先出FIFO,队头出队,队尾入队;
1.3.2常见实现类
(1).线程不安全
LinkedList
LinkedList基于“双向链表”实现的队列
PriorityQueue
PriorityQueue基于“堆”实现的优先队列
(2)线程安全
BlockingQueue
阻塞队列:
- ArrayBlockingQueue(有界)
- LinkedBlockingQueue(无界)