list,列表序列。是编程工作充常用的对象,类似数组,用于存储统一类型的集合的对象。
先放结论:
类型 | 数据结构 | 特点描述 |
---|---|---|
ArrayList | 数组 | 最常用,随机访问高效,插入删除效率低 |
LinkedList | 双向链表 | 随机访问效率低,但随机插入、随机删除效率低 |
Vector | 数组 | 类似ArrayList,但线程安全 |
Stack | 栈 | 它继承于Vector,先进后出 |
ArrayList
首先,ArrayList的本质是数组,在jdk1.8.0_121的java.util.ArrayList中,可以看到以下代码
transient Object[] elementData; // non-private to simplify nested class access
这就是ArrayList的本质,一个Object[],只不过,在add的时候,会自动的进行扩容而已。
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
也因此:
- 随机访问效率高(直接elementData[i]就可以取得对象)
- 插入删除慢(每次插入删除,尤其是对非尾对象进行改变时,都会导致Object[] elementData结构整个的变动)
- 线程不安全(单纯的数组,没有做任何保护处理)
推荐使用方法:
// ArrayList基本例子
List<String> arrayList = new ArrayList<String>();
arrayList.add("test1");
arrayList.add("test2");
arrayList.add("test3");
for (int i = 0; i < arrayList.size();i++){
System.out.println(arrayList.get(i));
}
LinkedList
源码(jdk1.8.0_121):
transient Node<E> first;
transient Node<E> last;
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;
}
}
是的,这就是双向链表,只保存首尾两个节点,同时,每个节点保存其自身前后节点地址。也因此:
- 访问效率低(非首尾节点时,只能从首尾节点开始,一个一个去寻找)
- 插入删除效率高(只需要创建或者删除节点,并且调整相邻节点的地址即可)
- 线程不安全(节点,没有做任何保护处理)
推荐使用方法:
// linkedList基本例子
List<String> linkedList = new LinkedList<String>();
linkedList.add("test1");
linkedList.add("test2");
linkedList.add("test3");
for (String data : linkedList){
System.out.println(data);
}
另外,用于LinkedList的implements包含Deque,而Deque继承Queue,所以LinkedList也可作为队列来使用。
例子:
Queue<String> queue = new LinkedList<String>();
queue.add("test1");
queue.add("test2");
queue.add("test3");
System.out.println(queue.size());
for (int i = 0; i < 3;i++){
System.out.println(queue.poll());
}
System.out.println(queue.size());
// 结果:
// 3
// test1
// test2
// test3
// 0
Vector
源码(jdk1.8.0_121):
protected Object[] elementData;
是的,和ArrayList一样,Vector的本质是一个数组,区别在于相关的方法:
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
对,重点就是synchronized,因为Vector的方法都有synchronized,有了线程的保护,所以Vector是线程安全的。
Vector类似ArrayList,是线程安全的。知道这一点就足够了。
** !但是! **
虽然Vector是线程安全的,但是想要使用线程安全的ArrayList的时候,简易使用JUC集合类中的CopyOnWriteArrayList。
Stack
源码(jdk1.8.0_121):
protected Object[] elementData;
又来?是的,Stack的底层仍然是一个数组,但是相关的方法却大不一样。虽然由于继承父类Vector,add这种方法还是存在,但是却一般不用。一般使用的是以下方法:
public E push(E item) {
addElement(item);
return item;
}
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
说明:
push:将元素推入栈中,通过将元素追加的数组的末尾中。
peek:取出栈顶元素,不执行删除,返回数组末尾的元素。
pop:取出栈顶元素,将该元素从栈中删除,返回。
总之:
- 使用push插入,用peek或pop去除(符合堆栈特性)
- 线程安全(使用synchronized)
例子:
// stack基本例子
Stack<String> stack = new Stack<String>();
stack.push("test1");
stack.push("test2");
stack.push("test3");
for (int i = 0; i < 3;i++){
System.out.println(stack.pop());
}