ArrayList、LinkedList、Vector区别

来源:互联网 发布:抢报名的软件 编辑:程序博客网 时间:2024/05/19 12:25

一、

这三者都实现了List 接口.所有使用方式也很相似,主要区别在于因为实现方式的不同,所以对不同的操作具有不同的效率。
ArrayList 是一个可改变大小的数组.当更多的元素加入到ArrayList中时,其大小将会动态地增长.内部的元素可以直接通过get与set方法进行访问,因为ArrayList本质上就是一个数组.
LinkedList 是一个双链表,在添加和删除元素时具有比ArrayList更好的性能.但在get与set方面弱于ArrayList.
当然,这些对比都是指数据量很大或者操作很频繁的情况下的对比,如果数据和运算量很小,那么对比将失去意义.
Vector 和ArrayList类似,但属于强同步类。如果你的程序本身是线程安全的(thread-safe,没有在多个线程之间共享同一个集合/对象),那么使用ArrayList是更好的选择。
Vector和ArrayList在更多元素添加进来时会请求更大的空间。Vector每次请求其大小的双倍空间,而ArrayList每次对size增长50%.
LinkedList 还实现了 Queue 接口,该接口比List提供了更多的方法,包括 offer(),peek(),poll()等.
注意: 默认情况下ArrayList的初始容量非常小,所以如果可以预估数据量的话,分配一个较大的初始值属于最佳实践,这样可以减少调整大小的开销。
3. ArrayList示例

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void testArrayList() {
    ArrayList<integer> al =new ArrayList<integer>();
    al.add(3);
    al.add(2);       
    al.add(1);
    al.add(4);
    al.add(5);
    al.add(6);
    al.add(6);
    Iterator<integer> iter1 = al.iterator();
    while(iter1.hasNext()){
        System.out.println(iter1.next());
    }
}</integer></integer></integer>
4. LinkedList示例
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void testLinkedList() {
    LinkedList<integer> ll =new LinkedList<integer>();
    ll.add(3);
    ll.add(2);       
    ll.add(1);
    ll.add(4);
    ll.add(5);
    ll.add(6);
    ll.add(6);
    Iterator<integer> iter2 = ll.iterator();
    while(iter2.hasNext()){
        System.out.println(iter2.next());
    }
}</integer></integer></integer>
如上面的例子所示,其使用方式是相似的,实际的区别在于底层的实现方式以及操作的复杂性不同.
5. Vector
Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized).因此,开销就比ArrayList要大.正常情况下,大多数的Java程序员使用ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。
6. ArrayList与LinkedList性能对比
时间复杂度对比如下:
 ArrayListLinkedListget()O(1)O(n)add()O(1)O(1) amortizedremove()O(n)O(n)



* 表中的 add() 代表 add(E e),而 remove()代表 remove(int index)'
ArrayList 对于随机位置的add/remove,时间复杂度为 O(n),但是对于列表末尾的添加/删除操作,时间复杂度是 O(1). LinkedList对于随机位置的add/remove,时间复杂度为 O(n),但是对于列表 末尾/开头 的添加/删除操作,时间复杂度是 O(1).

我使用下面的代码来测试他们的性能:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public static void testPerformance() {
    ArrayList<integer> arrayList =new ArrayList<integer>();
    LinkedList<integer> linkedList =new LinkedList<integer>();
    int
    times =10 * 1000;
    // times = 100 * 1000;
    // times = 1000 * 1000;
    System.out.println(Test times =  + times);
    System.out.println(-------------------------);
    // ArrayList add
    longstartTime = System.nanoTime();
    for(int i = 0; i < times; i++) {
        arrayList.add(i);
    }
    longendTime = System.nanoTime();
    longduration = endTime - startTime;
    System.out.println(duration +  <--ArrayList add);
    // LinkedList add
    startTime = System.nanoTime();
    for(int i = 0; i < times; i++) {
        linkedList.add(i);
    }
    endTime = System.nanoTime();
    duration = endTime - startTime;
    System.out.println(duration +  <--LinkedList add);
    System.out.println(-------------------------);
    // ArrayList get
    startTime = System.nanoTime();
    for(int i = 0; i < times; i++) {
        arrayList.get(i);
    }
    endTime = System.nanoTime();
    duration = endTime - startTime;
    System.out.println(duration +  <--ArrayList get);
    // LinkedList get
    startTime = System.nanoTime();
    for(int i = 0; i < times; i++) {
        linkedList.get(i);
    }
    endTime = System.nanoTime();
    duration = endTime - startTime;
    System.out.println(duration +  <--LinkedList get);
    System.out.println(-------------------------);
    // ArrayList remove
    startTime = System.nanoTime();
    for(int i = times - 1; i >=0; i--) {
        arrayList.remove(i);
    }
    endTime = System.nanoTime();
    duration = endTime - startTime;
    System.out.println(duration +  <--ArrayList remove);
    // LinkedList remove
    startTime = System.nanoTime();
    for(int i = times - 1; i >=0; i--) {
        linkedList.remove(i);
    }
    endTime = System.nanoTime();
    duration = endTime - startTime;
    System.out.println(duration +  <--LinkedList remove);
}</integer></integer></integer></integer>

输出结果如下:

?
1
2
3
4
5
6
7
8
9
10
Test times = 10000
-------------------------
1469985 <--ArrayList add
3530491 <--LinkedList add
-------------------------
593678 <--ArrayList get
86914251 <--LinkedList get
-------------------------
625651 <--ArrayList remove
2164320 <--LinkedList remove

?
1
2
3
4
5
6
7
8
9
10
Test times = 100000
-------------------------
11480805 <--ArrayList add
26384338 <--LinkedList add
-------------------------
714072 <--ArrayList get
10040809061<--LinkedList get
-------------------------
1203935 <--ArrayList remove
1595905 <--LinkedList remove

?
1
1000*1000次的运行中,很长时间过后, LinkedList的get日志还没有打印出来,大概是15分钟左右,结果还是没有出来.

?
1
2
3
4
5
6
7
8
9
10
Test times = 1000000
-------------------------
132632998 <--ArrayList add
322885939 <--LinkedList add
-------------------------
3690752 <--ArrayList get
1520315361147<--LinkedList get
-------------------------
8750043 <--ArrayList remove
13872885 <--LinkedList remove

他们性能的差异相当明显,LinkedList在 add和remove 上更快,而在get上更慢(原文是这样的).

译者注: 译者的编译和执行环境是 MyEclipse的JDK6,不论怎么看,都是 ArrayList更胜一筹,所以,该怎么选择,请根据自己的实际情况来决定,最好自己做测试,因为数据类型不同,JDK版本不同,优化不同,就可能有不同的结果。

根据时间复杂度表格,以及测试结果,我们可以判断何时该用ArrayList,何时该用LinkedList.

简单来说,LinkedList更适用于:
没有大规模的随机读取大量的增加/删除操作

二、演示各种List的使用

package com.sky.arrayset;
//ArrayList、Vector和LinkedList
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Stack;
import java.util.Vector;
/**
* 演示各种List的使用
* @author sky
*List能维护元素的次序,它允许元素重复
*/
public abstract class TestList {

/**初始化一个List*/
public static void init(List list){
  if(list!=null){
   list.add("aaa");
   list.add("ccc");
   list.add("bbb");
   list.add("eee");
   list.add("ddd");
  }
}

/**输出List的内容*/
public static void output(List list){
  if(list!=null){
   //根据列表下标遍历,使用list.size()获取列表中元素的个数
   for(int i=0;i<list.size();i++){
    System.out.println("\n\t遍历list:"+list.get(i));
   }
   //或者用迭代器遍历
   Iterator it=list.iterator();
   Object value=null;
   while(it.hasNext()){
    value=it.next();
   }
  }
  System.out.println();
}

/**使用ArrayList*/
public static void testArrayList(){
  List list=new ArrayList();
  init(list);
  System.out.println("\n\t使用ArrayList:");
  output(list);
}


/**使用Vector*/
public static void testVector(){
  List list=new Vector();
  init(list);
  System.out.println("\n\t使用Vector:");
  output(list);
}

/**使用LinkedList*/
public static void testLinkedList(){
  List list=new LinkedList();
  init(list);
  System.out.println("\n\t使用LinkedList");
  output(list);
}

public static void main(String[] args){
  TestList.testArrayList();
  TestList.testVector();
  TestList.testLinkedList();
 
  List list=new ArrayList();
  init(list);
  //List支持元素重复
  list.add("aaa");
  list.add("bbb");
  System.out.println("\n\t插入原aaa,bbb后:");
  output(list);
  //指定元素出入的位置
  list.add(1, "fff");
  System.out.println("在下标为1处插入fff后:");
  output(list);
 
  List list2=new ArrayList();
  list2.add("ggg");
  list2.add("hhh");
  //将另一个列表中的元素插入到列表中
  list.addAll(list2);
  System.out.println("添加list2大元素后:");
  output(list);
 
  //判断列表是否包含某一元素
  //通过元素的equals方法,判断元素是否相等
  System.out.println("\n\t list包含aaa?"+list.contains("aaa"));
  //判断列表中是否包含了另一个列表中的所有元素
  System.out.println("\n\t list包含list2中的所有元素?"+list.containsAll(list2));
  //定位一个元素在列表中最先出现的位置
  System.out.println("\n\t aaa在list中第一次出现的位置:"+list.indexOf("aaa"));
  //定位一个元素在列表中最后出现的位置
System.out.println("\n\t aaa在list中最后一次出现的位置:"+list.lastIndexOf("aaa"));
 
 
  //更新列表中某个位置的元素值
  list.set(2, "xxx");
  System.out.println("\n\t更新位置为2的元素为xxx后:");
  output(list);
  //删除列表中的某个元素,只删除第一次出现的出现的那个
  list.remove("aaa");
  System.out.println("\n\t删除元素aaa后");
  output(list);
  //删除列表中指定的位置的元素
  list.remove(1);
  System.out.println("\n\t删除下标为1的元素后");
  output(list);
  //删除列表中其他元素,只保留另一个列表中包含的元素
  list.retainAll(list2);
  System.out.println("\n\t删除list2包含以外的元素后");
  output(list);
  //删除列表中在另一个列表中也包含了的元素
  list.removeAll(list2);
  System.out.println("\n\t删除list2包含的元素后:");
  output(list);
 
  list.clear();//清空列表
  //判断列表是否有数据
  System.out.println("\n\t清空List后,list为空吗?"+list.isEmpty());
  init(list);
  //用列表中的某些元素构造一个新的列表
  list2=list.subList(1, 3);
  System.out.println("\n\t用list的第1个到第3个元素构造一个新的List:");
  output(list2);
 
  //用List特有的遍历器ListIterator遍历列表
  //与普通的Iterator不用,它允许从两个方向遍历列表
  ListIterator listIt=list.listIterator();
  System.out.println("\n\t正向遍历列表");
  while(listIt.hasNext()){
   System.out.println("\n\t正向遍历列表  :"+listIt.next());
  }
  System.out.println("\n\t");
  System.out.println("\n\t反向遍历列表");
  while(listIt.hasPrevious()){
   System.out.println("\n\t反向遍历列表:"+listIt.previous());
  }
  System.out.println();
  //也可以使用ListIterator从list中间插入和删除元素
  //只能在遍历当前位置进行添加和删除
  listIt.add("newadd");
  System.out.println("\n\t用ListIterator往列表中添加的元素newadd后:");
  output(list);
  listIt.next();
  listIt.remove();
  System.out.println("\n\t用ListIterator删除列表中元素后:");
  output(list);
 
 
  LinkedList linklist=new LinkedList();
  init(linklist);
  //添加元素到列表表头
  linklist.addFirst("fff");
  System.out.println("把fff放到列表表头后:");
  output(linklist);
  //添加元素到表尾
  linklist.addLast("eee");
  System.out.println("把eee放到列表尾后:");
  output(linklist);
 
  System.out.println("\n\t列表头元素:"+linklist.getFirst());
  System.out.println("\n\t列表尾元素:"+linklist.getLast());
  linklist.removeFirst();//删除列表表头的元素
  System.out.println("删除列表头元素后:");
  output(linklist);
  linklist.removeLast();//删除列表尾的元素
  System.out.println("删除列表的尾元素后:");
  output(linklist);
 
  Stack myStack=new Stack();
  //插入元素,是插入到尾部
  myStack.push("aaa");
  myStack.push("bbb");
  myStack.push("ccc");
  myStack.push("ddd");
  myStack.push("aaa");
  myStack.push("ddd");
  System.out.println("\n\t堆栈中的元素师:");
  output(myStack);
  System.out.println("\n\t堆栈尾部的元素:"+myStack.peek());
  System.out.println("\n\t堆栈头部的元素:"+myStack.pop());
}
}

三、

线性表,链表,哈希表是常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构。这些类均在java.util包中。

Collection是最基本的集合接口,一个Collection代表一组Object(即Collection的元素Elements)。JDK不提供直接继承自Collection的类,JDK提供的类都是继承自Collection的“子接口”如List,Set。

关系图如下:

Collection
List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
Set
Map

├Hashtable
├HashMap
└WeakHashMap

从关系图上看出,Collection接口派生的两个接口是List和Set。(Map没有继承Collection接口)

       对List的实现有3个,分别是Vector    LinkedList     ArrayList

LinkedList类

LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在 LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。

注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:

List list = Collections.synchronizedList(new LinkedList(...));

ArrayList类

ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。

size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。

每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并 没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。

和LinkedList一样,ArrayList也是非同步的(unsynchronized)。

Vector类
Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的 Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例 如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该 异常。

分析完后基本可以看出区别

Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好。
当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间
(1)ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能含有“空隙”。
当数组大小不满足时会增加存储能力,将已有数组数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行拷贝,移动,代价比较高。因此,它适合随即查找和遍历,不适合插入合删除。
(2)Vector与ArrayList一样,也是通过数组实现的。不同的是它支持线程的同步,
即一时刻只有一个线程能够写Vector,避免多线程同时写引起的不一致性。但实现同步需要很高的花费,
因此,访问它比访问ArrayList慢。
(3)LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随即访问和遍历速度比较慢。另外,它还提供了List没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。
(4)因为ArrayList和Vector都是用数组实现的,所以,在添加和插入时,最好从表尾操作,而不要从中间或者表头开始,以避免数组移动引起的开销!
(5)可以为每个List生成ListIterator,支持双向遍历List,而且能够在ListIterator位置插入和删除元素。
(6)堆栈类继承Vector,它总是对列表的尾部数据进行操作,采用了先进后出的策略,自定义了插入、查看和弹出元素三个方法。

 

0 0