集合框架图
有这个框架可以看出由Collection派生的两个接口List和set,Queue是java提供的队列实现类似于List
List接口
List是有序的Collection,并且允许重复,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
实现List接口的常用类有ArrayList、LinkedList、Vector。***
下面分别是ArrayList、LinkedList、Vector的区别和联系
底层实现方式
(1)ArrayList内部用数组来实现(是对数组的升级,是一个动态方法,提供了一些方法可以直接操作);继承AbstractList类;
(2)LinkedList内部采用双向链表实现;继AbstractSequentialList类;
(3)Vector内部用数组实现;继承AbstractList类。
读写扩容机制
(1)ArrayList:底层是一个动态的数组存在扩容说法;在执行插入元素是超过当前数组预定义的最大值时,数组需要扩容,扩容过程需要调用底层System.arraycopy()方法进行大量的数组复制操作;并且是1.5倍扩容;
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
(2)Vector底层是动态数组存在扩容的说法。对于Vector,默认创建一个大小为10的Object数组,并将capacityIncrement设置为0;当插入元素数组大小不够时,如果capacityIncrement> 0,则将Object数组的大小扩大为现有size+capacityIncrement;如果capacityIncrement<=0,则将Object数组的大小扩大为现有大小的2倍。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
(3)LinkedList:不存在扩容,因为底层是链表结构;
读写效率
(1)ArrayList对元素的增加和删除都会引起数组的内存分配空间动态发生变化。因此,对其进行插入和删除速度较慢,但检索速度很快。在查找元素时要遍历数组,对于非null的元素采取equals的方式寻找。
(2)Vector这点和ArrayList相似。
(3)LinkedList由于基于链表方式存放数据,增加和删除元素的速度较快,但是检索速度比较慢。在插入元素时,须创建一个新的对象,并更新相应元素的前后元素的引用;在查找元素时,需遍历链表;在删除元素时,要遍历链表,找到要删除的元素,然后从链表上将此元素删除即可。
线程安全性
(1)ArrayList、LinkedList为非线程安全;
(2)Vector是基于synchronized实现的线程安全的ArrayList。但是效率没有ArrayList和LinkedList高;
内存空间占用
(1)ArrayList的空间浪费主要体现在list列表结尾会预留一定的容量空间。
(2)Vector和ArrayList一样。
(3)LinkedList的空间花费则体现在它的每一个元素都要需要消耗比ArrayList多的空间(因为要储存直接前驱和直接后驱以及数据)。
数组和链表:
一.数组
数组静态分配内存。
缺点:在内存中,数组是一块连续的区域。数组需要预留空间,每次申请数组之前必须规定数组的大小,如果大小不合理,则可能会浪费内存。而且它对内存空间要求高,必须有足够的连续内存空间。而且数组大小固定,不能动态拓展。
优点:数组利用下标定位,时间复杂度为O(1),数组插入或删除元素的时间复杂度O(n)。
二.链表
链表动态分配内存。
优点:链表在内存中是不连续的,插入删除速度快(因为有next指针指向其下一个节点,通过改变指针的指向可以方便的增加删除元素)。内存利用率高,不会浪费内存(可以使用内存中细小的不连续空间(大于node节点的大小),并且在需要空间的时候才创建空间)。大小没有固定,拓展很灵活。
缺点:不能随机查找,必须从第一个开始遍历,查找效率低。