首页 > 代码库 > [知识整理]Java集合(一) - List

[知识整理]Java集合(一) - List

一、实现List的几个类:

ArrayList、LinkedList、CopyOnWriteArrayList、Vector

二、几个List底层的数据结构:

ArrayList - 数组列表

LinkedList - 双链表列表和队列(同时实现List和Queue接口)

Vector - 数组列表(加锁)

CopyOnWriteArrayList - 数组列表(读写分离)

三、List的几种基本操作比较

1、add操作比较

 1   /**
 2    * 比较add操作
 3    * @param minTimes 最小次数
 4    * @param maxTimes 最大次数
 5    * @param stepLen 步长
 6    */
 7   public static void compareAddMethod(int minTimes, int maxTimes, int stepLen) {
 8     Date begin = null;
 9     Date end = null;
10     int times = minTimes;
11     while(times < maxTimes) {
12       
13       //add: Vector
14       Vector<Integer> vector = new Vector<Integer>();
15       begin = new Date();
16       for(int i=0; i<times; i++) {
17         vector.add(i);
18       }
19       end = new Date();
20       System.out.println("Vector add "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");
21       
22       //add: ArrayList
23       ArrayList<Integer> arrayList = new ArrayList<Integer>();
24       begin = new Date();
25       for(int i=0; i<times; i++) {
26         arrayList.add(i);
27       }
28       end = new Date();
29       System.out.println("ArrayList add "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");
30       
31       //add: LinkedList
32       LinkedList<Integer> linkedList = new LinkedList<Integer>();
33       begin = new Date();
34       for(int i=0; i<times; i++) {
35         linkedList.add(i);
36       }
37       end = new Date();
38       System.out.println("LinkedList add "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");
39       
40       //add: CopyOnWriteArrayList
41       if(times<=100000) {
42         CopyOnWriteArrayList<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<Integer>();
43         begin = new Date();
44         for(int i=0; i<times; i++) {
45           copyOnWriteArrayList.add(i);
46         }
47         end = new Date();
48         System.out.println("CopyOnWriteArrayList add "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");
49       }
50       
51       System.out.println("-------------------------------");
52       times+=stepLen;
53     }

运算结果:
minTimes = 0, maxTimes = 100000, stepLen = 30000
times Vector(ms) ArrayList(ms) LinkedList(ms) CopyOnWriteArrayList(ms)
0 0 0 0 0
30000 3 2 2 339
60000 3 4 1 992
90000 1 1 0 2257

结果分析:这里可以看出随着增加执行次数的增加CopyOnWriteArrayList所耗时增加较多,这是由于CopyOnWriteArrayList的读写策略造成。CopyOnWriteArrayList进行写操作,先对原底层数组进行复制,然后在复制数组进行写操作,最后复制数组对原数组引进行替换。这里的耗时消耗在复制数组上面,数组元素越多,所消耗的时间就越多。

minTimes = 0, maxTimes = 10000000, stepLen = 1000000
times Vector(ms) ArrayList(ms) LinkedList(ms)
0 0 0 0
1000000 37 20 29
2000000 613 396 603
3000000 80 56 174
4000000 169 27 112
5000000 195 44 91
6000000 235 43 80
7000000 261 56 103
8000000 287 60 226
9000000 362 64 209

结果分析:这里ArrayList的插入比LinkedList快的原因是每次添加元素都是从数组末尾添加,数组直接通过索引在数组末尾添加元素,和LinkedList链表相比省却了寻找节点的时间,若是换成随机添加删除节点的操作,ArrayList就会比LinkedList慢了。而Vector虽然和ArrayList的底层数据结构一样(可变长的数组),但是由于Vector是线程安全的,这导致执行效率会比较慢。

2、remove操作比较

 1   /**
 2    * 比较remove操作
 3    * @param minTimes 最小次数
 4    * @param maxTimes 最大次数
 5    * @param stepLen 步长
 6    */
 7   public static void compareRemoveMethod(int minTimes, int maxTimes, int stepLen) {
 8     Date begin = null;
 9     Date end = null;
10     int times = minTimes;
11     while(times < maxTimes) {
12       
13       //remove: Vector
14       Vector<Integer> vector = new Vector<Integer>();
15       for(int i=0; i<times; i++)
16         vector.add(i);
17       begin = new Date();
18       for(int i=0; i<times; i++)
19         vector.remove(0);
20       end = new Date();
21       System.out.println("Vector remove "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");
22       
23       //remove: ArrayList
24       ArrayList<Integer> arrayList = new ArrayList<Integer>();
25       for(int i=0; i<times; i++)
26         arrayList.add(i);
27       begin = new Date();
28       for(int i=0; i<times; i++)
29         arrayList.remove(0);
30       end = new Date();
31       System.out.println("ArrayList remove "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");
32       
33       //remove: LinkedList
34       LinkedList<Integer> linkedList = new LinkedList<Integer>();
35       for(int i=0; i<times; i++)
36         linkedList.add(i);
37       begin = new Date();
38       for(int i=0; i<times; i++)
39         linkedList.remove(0);
40       end = new Date();
41       System.out.println("LinkedList remove "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");
42       
43       //remove: CopyOnWriteArrayList
44       if(times<=100000) {
45         CopyOnWriteArrayList<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<Integer>();
46         for(int i=0; i<times; i++)
47           copyOnWriteArrayList.add(i);
48         begin = new Date();
49         for(int i=0; i<times; i++)
50           copyOnWriteArrayList.remove(0);
51         end = new Date();
52         System.out.println("CopyOnWriteArrayList remove "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");
53       }
54       
55       System.out.println("-------------------------------");
56       times+=stepLen;
57     }
58   }
运算结果:
minTimes = 0, maxTimes = 100000, stepLen = 30000
times Vector(ms) ArrayList(ms) LinkedList(ms) CopyOnWriteArrayList(ms)
0 0 0 0 0
30000 90 90 5 229
60000 356 351 1 921
90000 832 834 0 3199

结果分析:不出意外,CopyOnWriteArrayList的插入删除操作依旧是耗时最长的。值得注意的是,这里LinkedList的优势体现出来了,删除插入随机节点时链表操作比数组操作是更有效率的。

3、读取操作比较

 1   /**
 2    * 比较读取操作
 3    * @param minTimes 最小次数
 4    * @param maxTimes 最大次数
 5    * @param stepLen 步长
 6    */
 7   public static void compareReadMethod(int minTimes, int maxTimes, int stepLen) {
 8     Date begin = null;
 9     Date end = null;
10     int times = minTimes;
11     while(times < maxTimes) {
12       
13       //remove: Vector
14       Vector<Integer> vector = new Vector<Integer>();
15       for(int i=0; i<times; i++)
16         vector.add(i);
17       begin = new Date();
18       for(int i=0; i<times; i++)
19         vector.get(i);
20       end = new Date();
21       System.out.println("Vector get "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");
22       
23       //get: ArrayList
24       ArrayList<Integer> arrayList = new ArrayList<Integer>();
25       for(int i=0; i<times; i++)
26         arrayList.add(i);
27       begin = new Date();
28       for(int i=0; i<times; i++)
29         arrayList.get(i);
30       end = new Date();
31       System.out.println("ArrayList get "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");
32       
33       //get: LinkedList
34       if(times<=100000) {
35         LinkedList<Integer> linkedList = new LinkedList<Integer>();
36         for(int i=0; i<times; i++)
37           linkedList.add(i);
38         begin = new Date();
39         for(int i=0; i<times; i++)
40           linkedList.get(i);
41         end = new Date();
42         System.out.println("LinkedList get "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");
43       }
44       
45       //get: CopyOnWriteArrayList
46       CopyOnWriteArrayList<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<Integer>();
47       for(int i=0; i<times; i++)
48         copyOnWriteArrayList.add(i);
49       begin = new Date();
50       for(int i=0; i<times; i++)
51         copyOnWriteArrayList.get(i);
52       end = new Date();
53       System.out.println("CopyOnWriteArrayList get "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");
54       
55       System.out.println("-------------------------------");
56       times+=stepLen;
57     }
58   }
运算结果:

minTimes = 0, maxTimes = 100000, stepLen = 30000
times Vector(ms) ArrayList(ms) LinkedList(ms) CopyOnWriteArrayList(ms)
0 0 0 0 0
30000 2 2 334 0
60000 1 1 1313 0
90000 0 0 2945 0

结果分析:这里为什么LinkedList耗时比较长就不需要说明,这里Vector如果在同时频繁写操作的时候读写耗时便会比较长,而由于CopyOnWriteArrayList使用读写分离测的策略(写操作在复制数组中进行,读操作在原数组里面进行)平衡了线程安全和执行性能的矛盾。

四、小结

1、在List元素数量比较少的时候没必要纠结采用哪个List,因为规模小时根本体现不出什么差别,此时要注意是否选取线程安全的List,一般情况是不采取线程安全的List,而是在ArrayList和LinkedList之间选择。

2、Vector - 底层数据结构为可变长数组,数组长度按100%增长,线程安全,写效率低,读效率在多线程环境下也低。

3、ArrayList - 底层数据结构为可变长数组,数组长度按50%增长,线程不安全,随机插入删除操作效率低,随机读取修改元素效率高。

4、LinkedList - 底层数据结构为双向链表,线程不安全,随机插入删除操作效率高,随机读取修改元素效率低。

5、CopyOnWriteArrayList - 底层数据结构为可变长数组,线程安全,写效率低,读效率在多线程环境下也高,但是由于读写分离,读数据可能出现过时的情况(不可重复读)

6、这里CopyOnWriteArrayList读写分离的思想值得我学习。

[知识整理]Java集合(一) - List