前言
Java有多种方式保存对象引用,如数组,但是数组具有固定尺寸,故提供了一些集合类(也称容器类)来解决这个问题。本文将详细的介绍List集合。
Java List集合简介
List是一种存储类的容器,它有一些特性:
- 集合中的元素是有序的
- 集合中的元素是可以重复
- 插入时可以选择具体的位置
- 可以通过索引来访问/操作元素
Java集合主要由2大体系构成,分别是Collection体系和Map体系,其中Collection和Map分别是2大体系中的顶层接口。
Collection主要有三个子接口,分别为List(列表)、Set(集)、Queue(队列)。其中,List、Queue中的元素有序可重复,而Set中的元素无序不可重复。
在List集合中,我们常用到是ArrayList和LinkedList这两个List的实现类
ArrayList
ArrayList底层基于数组实现
特点:
- 容量不固定,随元素增多而扩容, 扩容为时,先取旧容量的1.5倍,然后和新增数据之后的长度比较取大的
- 非线程安全的集合类
ArrayList扩容相关源码:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 取原有数组长度增加3/2,>>即二进制右移1位,相当于原数字大小除以2
int newCapacity = oldCapacity + (oldCapacity >> 1);
// newCapacity(旧容量的1.5倍)和minCapacity(新增数据之后的长度),谁大取谁
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果取得的最大的数比MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8(MAX_VALUE = 2147483647) 还大,(通常不会有这种情况
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);
}
LinkedList
LinkedList底层基于链表实现,双向链表结构 特点:
- 存储元素过程中,无需像 ArrayList 那样进行扩容,新增元素只需加上前后节点引用
- 实现Deque接口,具有双端队列的特性,既可以先进先出,也可以先进后出,所以也可以作为栈来使用
- 非线程安全的集合类
LinkedList中的单个元素node 相关源码:
private static class Node<E> {
// 当前节点数据
E item;
// 下一节点的指针
Node<E> next;
// 上一节点指针
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
ArrayList 和 LinkedList 比较(面试常问)
- 底层数据结构不同:ArrayList底层基于数组,LinkedList底层基于链表
- 效率不同:
- ArrayList底层基于数组,所以在随机访问元素(get、set)的时候效率高,直接通过下标取值,塞值。
- 插入和删除的时候(remove、add):如果删除或新增在末尾的话差不多;如果新增或删除在中间数据的话,LinkedList效率高,因为底层基于链表,所以只需要更改指针即可,而ArrayList需要通过移动数组来实现。
- 开销不同: LinkedList需要存储节点信息、前后节点指针信息。ArrayList当原数组满了的时候,会按原数组长度的3/2倍进行扩容,会存在一些预留空间。
示例比较
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class JavaListTest {
public static void main(String[] args) throws InterruptedException {
ArrayList<Integer> arrayList = new ArrayList<>();
LinkedList<Integer> linkedList = new LinkedList<>();
for (int i = 0; i < 10000; i++) {
arrayList.add(i);
linkedList.add(i);
}
System.out.println("ArrayList add Time = " + addData(arrayList));
System.out.println("LinkedList add Time = " + addData(linkedList));
System.out.println("ArrayList search Time = " + searchData(arrayList));
System.out.println("LinkedList search Time = " + searchData(linkedList));
}
public static Long addData(List list) {
Long start = System.currentTimeMillis();
for (int i = 10000; i < 20000; i++) {
list.add(i, i);
}
return System.currentTimeMillis() - start;
}
public static Long searchData(List list) {
Long start = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
list.get(i);
}
return System.currentTimeMillis() - start;
}
}
运行结果:
ArrayList add Time = 5
LinkedList add Time = 4
ArrayList search Time = 2
LinkedList search Time = 237