一、实现List的几个类:

ArrayList、LinkedList、CopyOnWriteArrayList、Vector

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

ArrayList - 数组列表

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

Vector - 数组列表(加锁)

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

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

1、add操作比较

   /**
    * 比较add操作
    * @param minTimes 最小次数
    * @param maxTimes 最大次数
    * @param stepLen 步长
    */
   public static void compareAddMethod(int minTimes, int maxTimes, int stepLen) {
     Date begin = null;
     Date end = null;
     int times = minTimes;
     while(times < maxTimes) {

       //add: Vector
       Vector<Integer> vector = new Vector<Integer>();
       begin = new Date();
       for(int i=0; i<times; i++) {
         vector.add(i);
       }
       end = new Date();
       System.out.println("Vector add "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");

       //add: ArrayList
       ArrayList<Integer> arrayList = new ArrayList<Integer>();
       begin = new Date();
       for(int i=0; i<times; i++) {
         arrayList.add(i);
       }
       end = new Date();
       System.out.println("ArrayList add "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");

       //add: LinkedList
       LinkedList<Integer> linkedList = new LinkedList<Integer>();
       begin = new Date();
       for(int i=0; i<times; i++) {
         linkedList.add(i);
       }
       end = new Date();
       System.out.println("LinkedList add "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");

       //add: CopyOnWriteArrayList
       if(times<=100000) {
         CopyOnWriteArrayList<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<Integer>();
         begin = new Date();
         for(int i=0; i<times; i++) {
           copyOnWriteArrayList.add(i);
         }
         end = new Date();
         System.out.println("CopyOnWriteArrayList add "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");
       }

       System.out.println("-------------------------------");
       times+=stepLen;
     }

运算结果:
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操作比较

   /**
    * 比较remove操作
    * @param minTimes 最小次数
    * @param maxTimes 最大次数
    * @param stepLen 步长
    */
   public static void compareRemoveMethod(int minTimes, int maxTimes, int stepLen) {
     Date begin = null;
     Date end = null;
     int times = minTimes;
     while(times < maxTimes) {

       //remove: Vector
       Vector<Integer> vector = new Vector<Integer>();
       for(int i=0; i<times; i++)
         vector.add(i);
       begin = new Date();
       for(int i=0; i<times; i++)
         vector.remove(0);
       end = new Date();
       System.out.println("Vector remove "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");

       //remove: ArrayList
       ArrayList<Integer> arrayList = new ArrayList<Integer>();
       for(int i=0; i<times; i++)
         arrayList.add(i);
       begin = new Date();
       for(int i=0; i<times; i++)
         arrayList.remove(0);
       end = new Date();
       System.out.println("ArrayList remove "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");

       //remove: LinkedList
       LinkedList<Integer> linkedList = new LinkedList<Integer>();
       for(int i=0; i<times; i++)
         linkedList.add(i);
       begin = new Date();
       for(int i=0; i<times; i++)
         linkedList.remove(0);
       end = new Date();
       System.out.println("LinkedList remove "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");

       //remove: CopyOnWriteArrayList
       if(times<=100000) {
         CopyOnWriteArrayList<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<Integer>();
         for(int i=0; i<times; i++)
           copyOnWriteArrayList.add(i);
         begin = new Date();
         for(int i=0; i<times; i++)
           copyOnWriteArrayList.remove(0);
         end = new Date();
         System.out.println("CopyOnWriteArrayList remove "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");
       }

       System.out.println("-------------------------------");
       times+=stepLen;
     }
   }
运算结果:
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、读取操作比较

   /**
    * 比较读取操作
    * @param minTimes 最小次数
    * @param maxTimes 最大次数
    * @param stepLen 步长
    */
   public static void compareReadMethod(int minTimes, int maxTimes, int stepLen) {
     Date begin = null;
     Date end = null;
     int times = minTimes;
     while(times < maxTimes) {

       //remove: Vector
       Vector<Integer> vector = new Vector<Integer>();
       for(int i=0; i<times; i++)
         vector.add(i);
       begin = new Date();
       for(int i=0; i<times; i++)
         vector.get(i);
       end = new Date();
       System.out.println("Vector get "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");

       //get: ArrayList
       ArrayList<Integer> arrayList = new ArrayList<Integer>();
       for(int i=0; i<times; i++)
         arrayList.add(i);
       begin = new Date();
       for(int i=0; i<times; i++)
         arrayList.get(i);
       end = new Date();
       System.out.println("ArrayList get "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");

       //get: LinkedList
       if(times<=100000) {
         LinkedList<Integer> linkedList = new LinkedList<Integer>();
         for(int i=0; i<times; i++)
           linkedList.add(i);
         begin = new Date();
         for(int i=0; i<times; i++)
           linkedList.get(i);
         end = new Date();
         System.out.println("LinkedList get "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");
       }

       //get: CopyOnWriteArrayList
       CopyOnWriteArrayList<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<Integer>();
       for(int i=0; i<times; i++)
         copyOnWriteArrayList.add(i);
       begin = new Date();
       for(int i=0; i<times; i++)
         copyOnWriteArrayList.get(i);
       end = new Date();
       System.out.println("CopyOnWriteArrayList get "+times+" times, cost: "+(end.getTime()-begin.getTime())+"ms");

       System.out.println("-------------------------------");
       times+=stepLen;
     }
   }
运算结果:

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的更多相关文章

  1. [知识整理]Java集合

    Mark Java集合图

  2. [知识整理]Java集合(二) - Set

    一.实现Set的几个类 HashSet.LinkedHashSet.TreeSet.ConcurrentSkipListSet.CopyOnWriterArraySet 二.对应底层的数据结构 Has ...

  3. 《Java基础知识》Java集合(Collection)

    作为一个Developer,Java集合类是我们在工作中运用最多的.最频繁的类.相比于数组(Array)来说,集合类的长度可变,更加适合于现代开发需求: Java集合就像一个容器,可以存储任何类型的数 ...

  4. 《Java基础知识》Java集合(Map)

    Java集合主要由2大体系构成,分别是Collection体系和Map体系,其中Collection和Map分别是2大体系中的顶层接口. 今天主要讲:Map主要有二个子接口,分别为HashMap.Tr ...

  5. Java基础知识(JAVA集合框架之List与Set)

    List和Set概述数组必须存放同一种元素.StringBuffer必须转换成字符串才能使用,如果想拿出单独的一个元素几乎不可能.数据有很多使用对象存,对象有很多,使用集合存. 集合容器因为内部的数据 ...

  6. 面试知识整理-Java基础

    三大特征:封装,继承,多态 多态:简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情. 抽象:抽象是将一类对象的共同特征总结出来构造类的过程 包装,可以讲基本类型当做对象来使用,抽象只关心对 ...

  7. 学习:java集合

    java是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由Sun Microsystems公司于1995年5月推出的Java程序设计语言和Java平台(即JavaEE, JavaME, Jav ...

  8. Java集合--HashMap分析

    HashMap在Java开发中有着非常重要的角色地位,每一个Java程序员都应该了解HashMap. 本文主要从源码角度来解析HashMap的设计思路,并且详细地阐述HashMap中的几个概念,并深入 ...

  9. Java集合框架相关知识整理

    1.常见的集合有哪些? Collection接口和Map接口是所有集合框架的父接口    Collection接口的子接口包括:Set接口和List接口    Map接口的实现类主要有:HashMap ...

随机推荐

  1. 使用canvas编写环形图.

    原理使用canvas画图: 第一步:画一个大圆 第二部:画一个扇形 第三部:画一个小圆 相互叠加. 最终效果: 现在上代码: (function($){ $.fn.drawPic=function(o ...

  2. 物料主数据MRP4中的独立/集中

    转自悲守穷庐 http://blog.itpub.net/12287/viewspace-681569/ 从按订单还是按库存来考虑. (1)独立集中为空,即又上层决定独立集中情况 (2)独立集中为1: ...

  3. [转载]Java程序员使用的20几个大数据工具

    最近我问了很多Java开发人员关于最近12个月内他们使用的是什么大数据工具. 这是一个系列,主题为: 语言web框架应用服务器SQL数据访问工具SQL数据库大数据构建工具云提供商今天我们就要说说大数据 ...

  4. Spark On YARN使用时上传jar包过多导致磁盘空间不够。。。

    今天测试过程中发现YARN Node变成Unhealthy了,后来定位到硬盘空间不够..... 通过查找大于100M的文件时发现有N多个spark-assembly-1.4.0-SNAPSHOT-ha ...

  5. asp.net中插件开发模式说明

    第一定义接口 /// <summary>          /// 这是插件必须实现的接口,也是主程序与插件通信的唯一接口         /// 换句话说,主程序只认识插件里的这些方法  ...

  6. zz Must read

    http://www.opengpu.org/forum.php?mod=viewthread&tid=965&extra=page%3D1 游戏引擎剖析(Game Engine An ...

  7. Mono 异步加载数据更新主线程

    主要是用 async和 await 调用 RunOnUiThread来更新. 调用函数: //异步加载数据开始 doInBackground (); //异步加载数据开始end protected a ...

  8. STL之序列式容器list与forward_list

    List (双向链表) 与 forwardlist (单向链表) 算是非常基础的数据结构了,这里只是简单介绍下其结构及应用. 以list为例: 其节点模板: template <class T& ...

  9. 整理一下自己用到的SVN几个命令

    第一步 svn co  代码分支(http://yyyyyyyyyyyyyyyy)  将开发给的代码分支地址中的代码拉到测试机中 第二步 cd /目录  进入需要拉代码的目录 然后 ll 查看目录下的 ...

  10. 用ionic快速开发hybird App(已附源码,在下面+总结见解)

    1.ionic简介 ionic 是用于敏捷开发APP的解决方案.核心思路是:利用成熟的前端开发技术,来写UI和业务逻辑.也就是说,就是一个H5网站,这个区别于react-native,native.即 ...