[Java]ArrayList、LinkedList、Vector、Stack的比较
一、介绍
先回顾一下List的框架图

由图中的继承关系,可以知道,ArrayList、LinkedList、Vector、Stack都是List的四个实现类。
AbstractList是一个抽象类,它继承于AbstractCollection。AbstractList实现List接口中除size()、get(int location)之外的函数。
AbstractSequentialList 是一个抽象类,它继承于AbstractList。AbstractSequentialList 实现了“链表中,根据index索引值操作链表的全部函数”。
ArrayList 是一个数组队列,相当于动态数组。它由数组实现,随机访问效率高,随机插入、随机删除效率低。
LinkedList 是一个双向链表。它也可以被当作堆栈、队列或双端队列进行操作。LinkedList随机访问效率低,但随机插入、随机删除效率低。
Vector 是矢量队列,和ArrayList一样,它也是一个动态数组,由数组实现。但是ArrayList是非线程安全的,而Vector是线程安全的。
Stack 是栈,它继承于Vector。它的特性是:先进后出(FILO, First In Last Out)。
二、性能测试
在对ArrayList、LinkedList、Vector、Stack进行比较之前,我们先来对他们进行一个性能测试,结合源码和测试结果来对ArrayList、LinkedList、Vector、Stack进行详细的分析。
import java.util.*;
public class ListTest {
private static final int COUNT = 100000;
private static LinkedList linkedList = new LinkedList();
private static ArrayList arrayList = new ArrayList();
private static Vector vector = new Vector();
private static Stack stack = new Stack();
public static void main(String[] args) {
// 换行符
System.out.println();
// 插入
insertByPosition(stack) ;
insertByPosition(vector) ;
insertByPosition(linkedList) ;
insertByPosition(arrayList) ;
// 换行符
System.out.println();
// 随机读取
readByPosition(stack);
readByPosition(vector);
readByPosition(linkedList);
readByPosition(arrayList);
// 换行符
System.out.println();
// 删除
deleteByPosition(stack);
deleteByPosition(vector);
deleteByPosition(linkedList);
deleteByPosition(arrayList);
}
// 获取list的名称
private static String getListName(List list) {
if (list instanceof LinkedList) {
return "LinkedList";
} else if (list instanceof ArrayList) {
return "ArrayList";
} else if (list instanceof Stack) {
return "Stack";
} else if (list instanceof Vector) {
return "Vector";
} else {
return "List";
}
}
// 向list的指定位置插入COUNT个元素,并统计时间
private static void insertByPosition(List list) {
long startTime = System.currentTimeMillis();
// 向list的位置0插入COUNT个数
for (int i=0; i<COUNT; i++)
list.add(0, i);
long endTime = System.currentTimeMillis();
long interval = endTime - startTime;
System.out.println(getListName(list) + " : insert "+COUNT+" elements into the 1st position use time:" + interval+" ms");
}
// 从list的指定位置删除COUNT个元素,并统计时间
private static void deleteByPosition(List list) {
long startTime = System.currentTimeMillis();
// 删除list第一个位置元素
for (int i=0; i<COUNT; i++)
list.remove(0);
long endTime = System.currentTimeMillis();
long interval = endTime - startTime;
System.out.println(getListName(list) + " : delete "+COUNT+" elements from the 1st position use time:" + interval+" ms");
}
// 根据position,不断从list中读取元素,并统计时间
private static void readByPosition(List list) {
long startTime = System.currentTimeMillis();
// 读取list元素
for (int i=0; i<COUNT; i++)
list.get(i);
long endTime = System.currentTimeMillis();
long interval = endTime - startTime;
System.out.println(getListName(list) + " : read "+COUNT+" elements by position use time:" + interval+" ms");
}
}
得到的结果如下
Stack : insert 100000 elements into the 1st position use time:834 ms
Vector : insert 100000 elements into the 1st position use time:818 ms
LinkedList : insert 100000 elements into the 1st position use time:10 ms
ArrayList : insert 100000 elements into the 1st position use time:822 ms
Stack : read 100000 elements by position use time:5 ms
Vector : read 100000 elements by position use time:3 ms
LinkedList : read 100000 elements by position use time:6088 ms
ArrayList : read 100000 elements by position use time:2 ms
Stack : delete 100000 elements from the 1st position use time:857 ms
Vector : delete 100000 elements from the 1st position use time:835 ms
LinkedList : delete 100000 elements from the 1st position use time:6 ms
ArrayList : delete 100000 elements from the 1st position use time:849 ms
根据结果,可以很明显的看出ArrayList、LinkedList、Vector、Stack的性能有很大的区别。
| 操作 | ArrayList | LinkedList | Vector | Stack |
|---|---|---|---|---|
| 读取 | 2ms | 6088ms | 3ms | 5ms |
| 插入 | 822ms | 10ms | 818ms | 834ms |
| 删除 | 849ms | 6ms | 835ms | 857ms |
读取:ArrayList > Vector > Stack > LinkedList
插入:LinkedList > Vector > ArrayList > Stack
删除:LinkedList > Vector > ArrayList > Stack
三、插入的分析
LinkedList
// 在index前添加节点,且节点的值为element
public void add(int index, E element) {
addBefore(element, (index==size ? header : entry(index)));
}
// 获取双向链表中指定位置的节点
private Entry<E> entry(int index) {
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
Entry<E> e = header;
// 获取index处的节点。
// 若index < 双向链表长度的1/2,则从前向后查找;
// 否则,从后向前查找。
if (index < (size >> 1)) {
for (int i = 0; i <= index; i++)
e = e.next;
} else {
for (int i = size; i > index; i--)
e = e.previous;
}
return e;
}
// 将节点(节点数据是e)添加到entry节点之前。
private Entry<E> addBefore(E e, Entry<E> entry) {
// 新建节点newEntry,将newEntry插入到节点e之前;并且设置newEntry的数据是e
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
// 插入newEntry到链表中
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;
}
从中,我们可以看出:通过add(int index, E element)向LinkedList插入元素时。先是在双向链表中找到要插入节点的位置index;找到之后,再插入一个新节点。
双向链表查找index位置的节点时,有一个加速动作:若index < 双向链表长度的1/2,则从前向后查找; 否则,从后向前查找。
ArrayList
// 将e添加到ArrayList的指定位置
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
ensureCapacity(size+1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
在这里面有一个非常耗时的操作
System.arraycopy(elementData, index, elementData, index + 1, size - index);
该方法被标记了native,调用了系统的C/C++代码,在JDK中是看不到的,但在openJDK中可以看到其源码。
该函数实际上最终调用了C语言的memmove()函数,因此它可以保证同一个数组内元素的正确复制和移动,比一般的复制方法的实现效率要高很多,很适合用来批量处理数组。Java强烈推荐在复制大量数组元素时用该方法,以取得更高的效率。
Vector
public synchronized void insertElementAt(E obj, int index) {
modCount++;
if (index > elementCount) {
throw new ArrayIndexOutOfBoundsException(index
+ " > " + elementCount);
}
ensureCapacityHelper(elementCount + 1);
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
elementData[index] = obj;
elementCount++;
}
public void add(int index, E element) {
insertElementAt(element, index);
}
可以看到Vector和ArrayList是一样的,都调用了System.arraycopy。由于Stack和继承与Vector,就不仔细分析了。
四、查找的分析
LinkedList
LinkedList随机访问的代码
// 返回LinkedList指定位置的元素
public E get(int index) {
return entry(index).element;
}
// 获取双向链表中指定位置的节点
private Entry<E> entry(int index) {
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
Entry<E> e = header;
// 获取index处的节点。
// 若index < 双向链表长度的1/2,则从前先后查找;
// 否则,从后向前查找。
if (index < (size >> 1)) {
for (int i = 0; i <= index; i++)
e = e.next;
} else {
for (int i = size; i > index; i--)
e = e.previous;
}
return e;
}
从中,我们可以看出:通过get(int index)获取LinkedList第index个元素时。先是在双向链表中找到要index位置的元素;找到之后再返回。
双向链表查找index位置的节点时,有一个加速动作:若index < 双向链表长度的1/2,则从前向后查找; 否则,从后向前查找。
ArrayList
// 获取index位置的元素值
public E get(int index) {
RangeCheck(index);
return (E) elementData[index];
}
private void RangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
}
我们可以看到ArrayList直接返回数组中index位置的元素,而不需要像LinkedList一样进行查找。
通过源码发现Vector和Stack的操作方式和ArrayList一样,这里就不详细分析了。
五、删除的分析
LinkedList
private E remove(Entry<E> e) {
if (e == header)
throw new NoSuchElementException();
E result = e.element;
e.previous.next = e.next;
e.next.previous = e.previous;
e.next = e.previous = null;
e.element = null;
size--;
modCount++;
return result;
}
由于删除了某一节点因此调整相应节点的前后指针信息,如下:
e.previous.next = e.next;//预删除节点的前一节点的后指针指向预删除节点的后一个节点。
e.next.previous = e.previous;//预删除节点的后一节点的前指针指向预删除节点的前一个节点。
清空预删除节点:
e.next = e.previous = null;
e.element = null;
交给gc完成资源回收,删除操作结束。
与ArrayList比较而言,LinkedList的删除动作不需要“移动”很多数据,从而效率更高。
ArrayList
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
恩,又是调用了System.arraycopy。
六、结论
| 操作 | ArrayList | LinkedList | Vector | Stack |
|---|---|---|---|---|
| 读取 | O(1) | O(n) | O(1) | O(1) |
| 插入 | O(n) | O(1) | O(n) | O(n) |
| 删除 | O(n) | O(1) | O(n) | O(n) |
- ArrayList(实现动态数组),查询快(随意访问或顺序访问),增删慢。整体清空快,线程不同步(非线程安全)。数组长度是可变的百分之五十延长
- LinkedList(实现链表),查询慢,增删快。
- Vector(实现动态数组),都慢,被ArrayList替代。长度任意延长。线程安全(同步的类,函数都是synchronized)
- Stack(实现堆栈)继承于Vector,先进后出。
所以,快速访问ArrayList,快速增删LinkedList,单线程都可以用,多线程只能用同步类Vector
[Java]ArrayList、LinkedList、Vector、Stack的比较的更多相关文章
- ArrayList LinkedList Vector
ArrayList是基于数组实现的,没有容量的限制. 在删除元素的时候,并不会减少数组的容量大小,可以调用ArrayList的trimeToSize()来缩小数组的容量. ArrayList, Lin ...
- Java ArrayList、Vector和LinkedList等的差别与用法(转)
Java ArrayList.Vector和LinkedList等的差别与用法(转) ArrayList 和Vector是采取数组体式格式存储数据,此数组元素数大于实际存储的数据以便增长和插入元素,都 ...
- ArrayList, LinkedList, Vector - dudu:史上最详解
ArrayList, LinkedList, Vector - dudu:史上最详解 我们来比较一下ArrayList, LinkedLIst和Vector它们之间的区别.BZ的JDK版本是1.7.0 ...
- Java ArrayList和Vector、LinkedList与ArrayList、数组(Array)和列表集合(ArrayList)的区别
ArrayList和Vector的区别ArrayList与Vector主要从二方面来说. 一.同步性: Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同步 ...
- java 中 ArrayList LinkedList Vector 三者的异同点
1.ArrayList和Vector都是基于数组实现的,所以查询速度很快,增加和删除(非最后一个节点)速度慢: Vector是线程安全的,ArrayList不是. 2.LinkedList 是一个双向 ...
- hasSet,TreeSet,ArrayList,LinkedList,Vector,HashMap,HashTable,TreeMap利用Iterator进行输出
基础类,没有重写hashCode()和equals()方法: package niukewang; import java.util.Objects; public class setClass { ...
- 集合类源码(二)Collection之List(ArrayList, LinkedList, Vector)
ArrayList 功能 完全命名 public class ArrayList<E> extends AbstractList<E> implements List<E ...
- ArrayList,LinkedList,Vector集合的认识
最近在温习Java集合部分,花了三天时间读完了ArrayList与LinkedList以及Vector部分的源码.之前都是停留在简单使用ArrayList的API,读完源码看完不少文章后总算是对原理方 ...
- 集合框架的详解,List(ArrayList,LinkedList,Vector),Set(HashSet,TreeSet)-(14)
集合详解: /* Collection |--List:元素是有序的,元素可以重复.因为该集合体系有索引. |--ArrayList:底层的数据结构使用的是数组结构.特点:查询速度很快.但是增删稍慢. ...
- List集合与Set集合(ArrayList,LinkedList,Vector,HashSet,LinkedHashSet,可变参数)
List集合介绍及常用方法 import java.util.ArrayList; import java.util.Iterator; import java.util.List; /* java. ...
随机推荐
- socket,获取html,webservice等,支持chunked,gzip,deflate
1. [代码][C#]代码using System;using System.Collections.Generic;using System.Linq;using System.Net.Socket ...
- css3立体旋转菜单
css3立体旋转菜单,css3,3D,立体旋转,立体菜单,菜单导航,css3立体旋转菜单是一款纯css3实现的三维立体旋转导航菜单. 源码下载页:http://www.huiyi8.com/sc/71 ...
- php设计模式课程---5、责任链模式是什么
php设计模式课程---5.责任链模式是什么 一.总结 一句话总结: 自己权限不够,就交给上级处理 1.选择结构怎么做到面向对象开闭原则? 也就是说if,都可以用接口的实现来实现,这样就避免了更新的时 ...
- zoj 3813 Alternating Sum(2014ACMICPC Regional 牡丹江站网络赛 E)
1.http://blog.csdn.net/dyx404514/article/details/39122743 思路:题目意思很清楚了,这里只说思路. 设区间[L,R],区间长度为len=(R-L ...
- <%!%>声明的变量和在<%%>中声明的变量的区别
通过写一个demo,查看Jsp_Servlet源码可知: <%!%>声明的变量是类似类的成员变量,<%%>中的变量是方法中的变量. 参考博客: http://www.cnblo ...
- linux中python easy_install命令
centos 下安装 python easy_installcurl -O http://peak.telecommunity.com/dist/ez_setup.pypython ez_setup. ...
- tcp攻击
- ACM学习历程——UVA127 "Accordian" Patience(栈, 链表)
Description ``Accordian'' Patience You are to simulate the playing of games of ``Accordian'' patie ...
- sql基础内容2
-- day16课程内容 --CREATE DATABASE day16;USE day16; -- *************一.数据约束********************------ 1.1 ...
- Redis的Spring配置讲解
Redis是一种特殊类型的数据库,他被称之为key-value存储 本文覆盖缓存和存储两方面进行说明,使用的是Spring 4.0和Java配置方式 代码地址下载地址:https://github.c ...