ArrayList和LinkedList的对比

在使用二分法排序的时候,ArrayList和LinkedList的性能出现了极大的差异,由此记录,以供警醒

总所周知,在java的集合中,ArrayList底层就是数组的形式,新增数据的时候需要如果数组大小超过默认值就需要扩容,由此产生的时间比较多,

而LinkedList使用链表的形式,在向末尾位置添加数据时不存在扩容的说法,由此运行速度比较快

所以linkedlist适合插入和删除数据,ArrayList由于底层原因,查找数组小标更容易找到,这也是仅仅表现在添加或删除末尾数据上的比较

而且如果将ArrayList的容器大小确定,再与LinkedList比较,此时ArrayList的添加速度都会比LinkedList快

 

于是我有了下面的测试想法,导入到数据模拟真实环境均是实体对象

 1 @Data  //可以省略getter和setter
 2 public class Article implements Comparable<Article>
 3         , Comparator<Article>
 4 {
 5 
 6     private int id;
 7 
 8     private int userId;
 9 
10     private double value;
11 
12     private String userName;
13 
14     public Article(){}
15 
16     public Article(int id,int userId,double value){
17         this();
18         this.id = id;
19         this.userId = userId;
20         this.value = value;
21     }
22 
23     public Article(int id,int userId,double value,String userName){
24         this();
25         this.id = id;
26         this.userId = userId;
27         this.value = value;
28         this.userName = userName;
29     }
30 
31     @Override
32     public int compareTo(Article o) {
33         if (o.getId() > this.getId())
34             return -1;
35         else if (o.getId() < this.getId())
36             return 1;
37         else
38             return 0;
39     }
40 
41     @Override
42     public int compare(Article o1, Article o2) {
43         if (o1.getId() < o2.getId())
44             return -1;
45         else if (o1.getId() > o2.getId())
46             return 1;
47         else
48             return 0;
49     }
50 }
Article实体类对象
构建生成数据方法
代码中有一次书写问题,就是创建ArrayList的时候,我们可以根据number来确定长度,而不是默认或固定大小
 1 public Map get(int number){
 2         List array = new ArrayList<Article>(100000);
 3         List list = new LinkedList();
 4         int slow = 0;
 5         long arrayTime = 0;
 6         long linkedTime = 0;
 7         for(int i = 0;i<number;i++) {
 8             int nextInt = new Random().nextInt(number);
 9             Article article = new Article(new Random().nextInt(number),
10                     new Random().nextInt(number),
11                     Math.round(new Random().nextDouble()) * 5l,
12                     "user" + ('a' + nextInt));
13             long begin = System.nanoTime();
14             list.add(article);
15             long middle = System.nanoTime();
16             array.add(article);
17             long end = System.nanoTime();
18             arrayTime += end - middle;
19             linkedTime += middle - begin;
20         }
21         System.out.println("linkedlist总耗时:"+linkedTime+",arraylist总耗时:"+arrayTime);
22         return new HashMap(){{put("arraylist",array);put("linkedlist",list);}};
23     }
生成数据方法

 

最后开始写排序的的二分法,分别用不同的两种list来实现

 1 public List binaryArrayListSorted(ArrayList<Article> articles){
 2         ArrayList<Article> list = new ArrayList<>();
 3         articles.forEach(new Consumer<Article>() {
 4             @Override
 5             public void accept(Article article) {
 6                 if(list.size()==0)
 7                     list.add(article);
 8                 else if(list.get(0).compareTo(article)>=0){
 9                     list.add(0,article);
10                 }else if(list.get(list.size()-1).compareTo(article)<=0){
11                     list.add(article);
12                 }else {
13                     int begin = 0;
14                     int end = list.size()-1;
15                     int middle = end/2;
16                     while(begin<middle&&middle<end){
17                         if(article.compareTo(list.get(middle))>0){
18                             begin = middle;
19                         }else if(article.compareTo(list.get(middle))<0){
20                             end = middle;
21                         }else{
22                             end = middle;
23                             break;
24                         }
25                         middle = (begin+end)/2;
26                     }
27 
28                     long time = System.nanoTime();
29                     list.add(article);
30                     if(list.size()%500==0) {
31                         System.out.println(System.nanoTime() - time);
32                     }
33                 }
34             }
35         });
36         return list;
37     }
ArrayList Sorted

 

 1 public LinkedList<T> binaryLinkedListSort(LinkedList<T> list){
 2         Iterator<T> iterator = list.iterator();
 3         LinkedList<T> linkedList = new LinkedList<>();
 4         while(iterator.hasNext()){
 5             T next = iterator.next();
 6             int begin = 0;
 7             int end = linkedList.size()-1;
 8             int middle = end + begin >> 1;
 9             if(linkedList.size()==0||next.compareTo(linkedList.get(begin))<=0){
10                 linkedList.add(begin,next);
11             }else if(next.compareTo(linkedList.get(end))>=0){
12                 linkedList.add(next);
13             }else {
14                 while ((end > middle) && (middle > begin)){
15                     if(next.compareTo(linkedList.get(middle))>0){
16                         begin = middle;
17                     }else if(next.compareTo(linkedList.get(middle))<0){
18                         end = middle;
19                     }else{
20                         end = middle;
21                         break;
22                     }
23                     middle = end + begin >> 1;
24                 }
25 
26                 long time = System.nanoTime();
27                 linkedList.add(next);
28                 if(linkedList.size()%500==0) {
29                     System.out.println(System.nanoTime() - time);
30                 }
31             }
32         }
33         return linkedList;
34     }
LinkedList Sorted

 

使用main方法去运行比较

 1 public class CompareSorted<T extends Comparable
 2         & Comparator<? super T>
 3         > {
 4 //因为用到了Arrays.sort(Compartor)方法,所以主类的泛型要继承Compartor
 5     public static void main(String[] args) {
 6         CompareSorted<Article> acs = new CompareSorted<Article>();
 7         Map map = acs.get(10000);
 8         LinkedList<Article> list = (LinkedList) map.get("linkedlist");
 9         ArrayList<Article> array = (ArrayList) map.get("arraylist");
10         Article[] arr = array.toArray(new Article[array.size()]);
11         long begin1 = System.currentTimeMillis();
12         acs.help(array);
13         System.out.println("ArrayList二分法排序用时:"+(System.currentTimeMillis()-begin1)+"ms");
14         long begin2 = System.currentTimeMillis();
15         acs.binarySort(list);
16         System.out.println("LinkedList二分法排序用时:"+(System.currentTimeMillis()-begin2)+"ms");
17         long begin = System.currentTimeMillis();
18         //这里array排序后不能再被后面使用,因为已经改变了原来的数据顺序
19         array.sort(array.get(0));
20         System.out.println("集合的普通排序用时:"+(System.currentTimeMillis()-begin)+"ms");
21         long begin3 = System.currentTimeMillis();
22         Arrays.sort(arr);
23         System.out.println("Arrays的排序用时:"+(System.currentTimeMillis()-begin3)+"ms");
24         long begin4 = System.currentTimeMillis();
25         Collections.sort(list);
26         System.out.println("Collections的排序用时:"+(System.currentTimeMillis()-begin4)+"ms");
27 }
28 }
main方法

 

分别跑不同的数据大小获得下面的结果(在数据大于5w的时候就不在输出LinkedList了)

插入10000条数据linkedlist总耗时:952800ns,arraylist总耗时:3923500ns
ArrayList二分法排序用时:18ms
LinkedList二分法排序用时:998ms
集合的普通排序用时:10ms
Arrays的排序用时:8ms
Collections的排序用时:7ms

  

插入50000条数据linkedlist总耗时:5983000ns,arraylist总耗时:2946500ns
ArrayList二分法排序用时:161ms
LinkedList二分法排序用时:82140ms
集合的普通排序用时:30ms
Arrays的排序用时:28ms
Collections的排序用时:31ms

  

插入100000条数据linkedlist总耗时:5876600ns,arraylist总耗时:4923100ns
ArrayList二分法排序用时:601ms
集合的普通排序用时:58ms
Arrays的排序用时:109ms
Collections的排序用时:67ms

 

插入500000条数据linkedlist总耗时:20399900ns,arraylist总耗时:17101100ns
ArrayList二分法排序用时:15659ms
集合的普通排序用时:215ms
Arrays的排序用时:308ms
Collections的排序用时:204ms

  

插入1000000条数据linkedlist总耗时:36756400ns,arraylist总耗时:33962300ns
ArrayList二分法排序用时:61723ms
集合的普通排序用时:419ms
Arrays的排序用时:559ms
Collections的排序用时:489ms

  

 

总结:

  1.在末尾添加数据,ArrayList正常情况都会比LinkedList快,而我们说的慢往往是因为ArrayList开辟容器大小时花费的时间更多

    (ArrayList添加元素直接在数组上新增,linkedlist链表添加需要执行两步,分别是指定最后一个元素的末位置是新元素的head,链表最后一个为新元素,这里可以参考 链表和数组结构)

  2.在指定位置添加数据(就是测试代码中的二分法排序,ArrayList是找到指定的位置后,使用System.arraycopy()方法复制数组前后的数据,然后让拼接成新的组合数组,而LinkedList虽然插入快速,

    但是光查询指定位置就十分耗费时间,所以在排序5w条数据时,LinkedList整整花费了82s的时间,如果数据更多,上千万或上亿级数据使用二分法是非常不适用的,

    于是我们后面会记录jdk1.8的排序方式:双枢快排)

 

posted @ 2019-05-27 16:23  嬴安  阅读(618)  评论(1编辑  收藏  举报