面试者之所以经常拿这三者来比较,是因为这三者都实现了List接口,他们的使用方式也很相似,主要区别在于因为实现方式的不同,所以对不同的操作具有不同的效率。
其实还有一种类型也要需要来比较的,那就Stack,没错,就是栈,它也属于List,他继承于Vector。
List接口有话讲,它属于有序的Collection,List允许有相同的元素,而另一个继承的Set他是不允许有相同的元素的,我们来看一下Collection集合接口的关系:
Collection ├List │├LinkedList │├ArrayList │└Vector │ └Stack └Set
下面先对各自的特性进行文字描述下:
ArrayList是实现了基于动态数组的数据结构,它由数组实现,随机访问效率高,随机插入、随机删除效率低,ArrayList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动。他线程不同步。
LinkedList基于链表的数据结构,LinkedList 是一个双向链表。它也可以被当作堆栈、队列或双端队列进行操作。LinkedList随机访问元素效率低,但随机插入元素、随机删除元素效率高,因为LinkedList随机访问数据是要按序号索引数据进行向前或向后遍历,然则插入数据时只须要记录本项的前后项即可,所以插入数度较快,LinkedList的中间插入或删除一个元素的开销是固定的。在LinkedList中有一个私有的内部类,定义如下:
private static class Entry { Object element; Entry next; Entry previous; }
Vector 也是实现了基于动态数组的数据结构,矢量队列。但是ArrayList是非线程安全的,而Vector是线程安全的。Vector和ArrayList在更多元素添加进来时会请求更大的空间。Vector每次请求其大小的双倍空间,而ArrayList每次对size增长50%。
Stack 是栈,Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。
数据比较执行代码:
import java.util.*; public class TestList { 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) { // 插入 insert(linkedList); insert(arrayList); insert(vector); insert(stack); System.out.println(); // 随机读取 read(linkedList); read(arrayList); read(vector); read(stack); System.out.println(); // 删除 delete(linkedList); delete(arrayList); delete(vector); delete(stack); System.out.println(); } 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个元素,并统计时间,还有一种情况要另外分析:在List列表未添加数据那这四种都差不多时间 private static void insert(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"); } // 根据position,不断从list中读取元素,并统计时间 private static void read(List list) { long startTime = System.currentTimeMillis(); 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"); } // 从list的指定位置删除COUNT个元素,并统计时间 private static void delete(List list) { long startTime = System.currentTimeMillis(); 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"); } }
执行100000次的结果:
LinkedList : insert 100000 elements into the 1st position use time:7 ms ArrayList : insert 100000 elements into the 1st position use time:1059 ms Vector : insert 100000 elements into the 1st position use time:1016 ms Stack : insert 100000 elements into the 1st position use time:1018 ms LinkedList : read 100000 elements by position use time:3983 ms ArrayList : read 100000 elements by position use time:1 ms Vector : read 100000 elements by position use time:3 ms Stack : read 100000 elements by position use time:3 ms LinkedList : delete 100000 elements from the 1st position use time:4 ms ArrayList : delete 100000 elements from the 1st position use time:962 ms Vector : delete 100000 elements from the 1st position use time:965 ms Stack : delete 100000 elements from the 1st position use time:969 ms
结果可见:
插入10万个数据(都插入0下标),LinkedList所花时间最短:7ms。其他三个差不多1000ms
遍历读取10万个数据(从0下标开始读起),LinkedList所花时间最长:3983 ms;而其他相差不多,都只用了几ms。
删除10万个数据(从0下标开始删除),LinkedList所花时间最短:4ms。其他对比LinkedList就多了了,都差不多花了960 ms。
最后总结下:
选择那种List,要结合业务的需要进行选择:
如果涉及到堆栈,队列等操作,应该考虑用List,对于随机访问比较多的话一定要用ArrayList而不是LinkedList,如果需要频繁的插入和删除应该考虑用LinkedList来提高性能。
如果业务在单线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。
尽量在开发业务逻辑做返回数据的时候,返回List接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。