[知识整理]Java集合 - List
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[知识整理]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的主要内容,如果未能解决你的问题,请参考以下文章