LinkedList 的一个错误用法

来源:互联网 发布:大华网络监控 编辑:程序博客网 时间:2024/05/01 05:59

最近在做图像处理的时候,发现某一段代码非常的慢,慢得让人无法接受。主要的代码是顺序访问一个LinkedList的元素,效果是随着index的变大,速度越来越慢,list的元素个数在百万以上。找到原因,分享出来,也希望大家不要跳入同一个陷阱。还是那一句话,可运行的代码和高质量的代码之间还是有比较远的距离。

LinkedList错误用法示例

代码里面加入了一些打印时间相关的代码,主要是为了直观的显示运行的耗时。

错误代码

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public static void main(String[] args) {  
  2.     // add elements  
  3.     int size = 2000000;  
  4.     List<String> list = new LinkedList<String>();  
  5.     for (int i = 0; i < size; i++) {  
  6.         list.add("Just some test data");  
  7.     }  
  8.   
  9.     long startTime = System.currentTimeMillis();  
  10.     for (int i = 0; i < size; i++) {  
  11.         list.get(i);  
  12.   
  13.         if (i % 10000 == 0) {  
  14.             System.out.println("query 10000 elements spend: "  
  15.                     + (System.currentTimeMillis() - startTime));  
  16.             startTime = System.currentTimeMillis();  
  17.         }  
  18.     }  
  19. }  

控制台输出如下:


错误原因

错误的代码就是list.get(i),LinkedList的底层是一个链表,随机访问i的时候,链表只能从头往后数,第i个才返回。所以时间随着i的变大时间会越来越长。

正确用法

顺序访问,LinkedList绝对不要用get方法,即使LinkedList的元素个数只有很少的几个。养成好习惯,免得犯错。

for each

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. for (String element : list) {  
  2.     // process element here  
  3. }  

iterator

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. Iterator<String> iter = list.iterator();  
  2. while (iter.hasNext()) {  
  3.     String element = iter.next();  
  4.     // process element here  
  5. }  

直接换为ArrayList

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public static void main(String[] args) {  
  2.     // add elements  
  3.     int size = 2000000;  
  4.     List<String> list = new ArrayList<String>();  
  5.     for (int i = 0; i < size; i++) {  
  6.         list.add("Just some test data");  
  7.     }  
  8.   
  9.     long startTime = System.currentTimeMillis();  
  10.     for (int i = 0; i < size; i++) {  
  11.         list.get(i);  
  12.   
  13.         if (i % 10000 == 0) {  
  14.             System.out.println("query 10000 elements spend: "  
  15.                     + (System.currentTimeMillis() - startTime));  
  16.             startTime = System.currentTimeMillis();  
  17.         }  
  18.     }  
  19. }  

ArrayList的控制台输出如下:


LinkedList VS ArrayList

下面比较一下LinkedList和ArrayList的效率。

新增、查询、删除比较

ArrayList测试代码如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public static void main(String[] args) {  
  2.     // add elements  
  3.     int size = 20000000;  
  4.     List<String> list = new ArrayList<String>();  
  5.     long startTime = System.currentTimeMillis();  
  6.     for (int i = 0; i < size; i++) {  
  7.         list.add("Just some test data");  
  8.     }  
  9.     System.out.println("add " + size + " elements spend: "  
  10.             + (System.currentTimeMillis() - startTime));  
  11.   
  12.     // query  
  13.     startTime = System.currentTimeMillis();  
  14.     String median = list.get(size / 2);  
  15.     System.out.println("query median spend: "  
  16.             + (System.currentTimeMillis() - startTime));  
  17.   
  18.     // delete  
  19.     startTime = System.currentTimeMillis();  
  20.     list.remove(median);  
  21.     System.out.println("delete median spend: "  
  22.             + (System.currentTimeMillis() - startTime));  
  23. }  

LinkedList测试代码如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public static void main(String[] args) {  
  2.     // add elements  
  3.     int size = 20000000;  
  4.     List<String> list = new LinkedList<String>();  
  5.     long startTime = System.currentTimeMillis();  
  6.     for (int i = 0; i < size; i++) {  
  7.         list.add("Just some test data");  
  8.     }  
  9.     System.out.println("add " + size + " elements spend: "  
  10.             + (System.currentTimeMillis() - startTime));  
  11.   
  12.     // query  
  13.     startTime = System.currentTimeMillis();  
  14.     String median = list.get(size / 2);  
  15.     System.out.println("query median spend: "  
  16.             + (System.currentTimeMillis() - startTime));  
  17.   
  18.     // delete  
  19.     startTime = System.currentTimeMillis();  
  20.     list.remove(median);  
  21.     System.out.println("delete median spend: "  
  22.             + (System.currentTimeMillis() - startTime));  
  23. }  

各自特点

  • 新增
    ArrayList比LinkedList快很多,超过一个数量级。很是意外。
  • 随机查询
    在i值很大的时候,ArrayList比LinkedList快很多,i越大,差距越大。ArrayList底层是数组,随机访问时间效率是O(0),而LinkedList是O(n)。
  • 删除
    LinkedList比ArrayList快很多。LinkedList的删除操作时间效率为O(0),而ArrayList是O(n),ArrayList需要查找数据、移动数据,所以慢。

总结

尽量使用ArrayList,ArrayList满足不了需求的时候再用LinkedList。根据LinkedList的特点,在下面几种情况下才使用LinkedList。

  • 需要使用java.util.List接口之外的API
    LinkedList实现了Queue和Stack等接口,可以用来当作一些特殊的容器。吐槽JDK里面LinkedList的设计,塞太多东西了,和名字不符。
  • 元素删除比较频繁
    如果数据量大,删除频繁,只能用LinkedList。
  • 内存碎片化且元素很多
    ArrayList底层是一个数组,数组要求一段连续的内存快。LinkedList也可以充分利用内存的一些碎片。特别是JVM使用Concurrent Mark-Sweep Collector垃圾回收器的时候,显得尤为重要。
0 0
原创粉丝点击