SDK提供了有序集合接口java.util.List的几种实现,其中三种最为人们熟知的是Vector、ArrayList和LinkedList。有关这些List类的性能差别是一个经常被问及的问题。在这篇文章中,我要探讨的就是LinkedList和Vector/ArrayList之间的性能差异。
  为全面分析这些类之间的性能差异,我们必须知道它们的实现方法。因此,接下来我首先从性能的角度出发,简要介绍这些类的实现特点。
  一、Vector和ArrayList的实现
  Vector和ArrayList都带有一个底层的Object[]数组,这个Object[]数组用来保存元素。通过索引访问元素时,只需简单地通过索引访问内部数组的元素:
  public Object get(int index)
  { //首先检查index是否合法...此处不显示这部分代码 return
  elementData[index]; }
  内部数组可以大于Vector/ArrayList对象拥有元素的数量,两者的差值作为剩余空间,以便实现快速添加新元素。有了剩余空间,添加元素变得非常简单,只需把新的元素保存到内部数组中的一个空余的位置,然后为新的空余位置增加索引值:
  public boolean add(Object o)
  { ensureCapacity(size + 1); //稍后介绍 elementData[size++] = o; return true;
  //List.add(Object) 的返回值 }
  把元素插入集合中任意指定的位置(而不是集合的末尾)略微复杂一点:插入点之上的所有数组元素都必须向前移动一个位置,然后才能进行赋值:
  public void add(int index, Object element) {
  //首先检查index是否合法...此处不显示这部分代码
  ensureCapacity(size+1);
  System.arraycopy(elementData, index, elementData, index + 1,
  size - index);
  elementData[index] = element;
  size++;
  }
  剩余空间被用光时,如果需要加入更多的元素,Vector/ArrayList对象必须用一个更大的新数组替换其内部Object[]数组,把所有的数组元素复制到新的数组。根据SDK版本的不同,新的数组要比原来的大50%或者100%(下面显示的代码把数组扩大100%):
  public void ensureCapacity(int minCapacity) {
  int oldCapacity = elementData.length;
  if (minCapacity > oldCapacity) {
  Object oldData[] = elementData;
  int newCapacity = Math.max(oldCapacity * 2, minCapacity);
  elementData = new Object[newCapacity];
  System.arraycopy(oldData, 0, elementData, 0, size);
  }
  }
  Vector类和ArrayList类的主要不同之处在于同步。除了两个只用于串行化的方法,没有一个ArrayList的方法具有同步执行的能力;相反,Vector的大多数方法具有同步能力,或直接或间接。因此,Vector是线程安全的,但ArrayList不是。这使得ArrayList要比Vector快速。对于一些最新的JVM,两个类在速度上的差异可以忽略不计:严格地说,对于这些JVM,这两个类在速度上的差异小于比较这些类性能的测试所显示的时间差异。
  通过索引访问和更新元素时,Vector和ArrayList的实现有着卓越的性能,因为不存在除范围检查之外的其他开销。除非内部数组空间耗尽必须进行扩展,否则,向列表的末尾添加元素或者从列表的末尾删除元素时,都同样有着优秀的性能。插入元素和删除元素总是要进行数组复制(当数组先必须进行扩展时,需要两次复制)。被复制元素的数量和[size-index]成比例,即和插入/删除点到集合中最后索引位置之间的距离成比例。对于插入操作,把元素插入到集合最前面(索引0)时性能最差,插入到集合最后面时(最后一个现有元素之后)时性能最好。随着集合规模的增大,数组复制的开销也迅速增加,因为每次插入操作必须复制的元素数量增加了。
  二、LinkedList的实现
  LinkedList通过一个双向链接的节点列表实现。要通过索引访问元素,你必须查找所有节点,直至找到目标节点:
  public Object get(intindex) {
  //首先检查index是否合法...此处不显示这部分代码
  Entry e = header; //开始节点
  //向前或者向后查找,具体由哪一个方向距离较
  //近决定
  if (index < size/2) {
  for (int i = 0; i <= index; i++)
  e = e.next;
  } else {
  for (int i = size; i > index; i--)
  e = e.previous;
  }
  return e;
  }
  把元素插入列表很简单:找到指定索引的节点,然后紧靠该节点之前插入一个新节点:
  public void add(int index, Object element) {
  //首先检查index是否合法...此处不显示这部分代码
  Entry e = header; //starting node
  //向前或者向后查找,具体由哪一个方向距离较
  //近决定
  if (index < size/2) {
  for (int i = 0; i <= index; i++)
  e = e.next;
  } else {
  for (int i = size; i > index; i--)
  e = e.previous;
  }
  Entry newEntry = new Entry(element, e, e.previous);
  newEntry.previous.next = newEntry;
  newEntry.next.previous = newEntry;
  size++;
  }
  线程安全的LinkedList和其他集合
  如果要从Java SDK得到一个线程安全的LinkedList,你可以利用一个同步封装器从Collections.synchronizedList(List)得到一个。然而,使用同步封装器相当于加入了一个间接层,它会带来昂贵的性能代价。当封装器把调用传递给被封装的方法时,每一个方法都需要增加一次额外的方法调用,经过同步封装器封装的方法会比未经封装的方法慢二到三倍。对于象搜索之类的复杂操作,这种间接调用所带来的开销不是很突出;但对于比较简单的方法,比如访问功能或者更新功能,这种开销可能对性能造成严重的影响。
  这意味着,和Vector相比,经过同步封装的LinkedList在性能上处于显著的劣势,因为Vector不需要为了线程安全而进行任何额外的间接调用。如果你想要有一个线程安全的LinkedList,你可以复制LinkedList类并让几个必要的方法同步,这样你可以得到一个速度更快的实现。对于所有其它集合类,这一点都同样有效:只有List和Map具有高效的线程安全实现(分别是Vector和Hashtable类)。有趣的是,这两个高效的线程安全类的存在只是为了向后兼容,而不是出于性能上的考虑。
  对于通过索引访问和更新元素,LinkedList实现的性能开销略大一点,因为访问任意一个索引都要求跨越多个节点。插入元素时除了有跨越多个节点的性能开销之外,还要有另外一个开销,即创建节点对象的开销。在优势方面,LinkedList实现的插入和删除操作没有其他开销,因此,插入-删除开销几乎完全依赖于插入-删除点离集合末尾的远近。

几种list集合的区别的更多相关文章

  1. HashMap、Hashtable、HashSet三种hash集合的区别

    转载:http://www.cnblogs.com/lzrabbit/p/3721067.html#h1 HashMap和Hashtable的区别 两者最主要的区别在于Hashtable是线程安全,而 ...

  2. UI:字典的两种取值的区别

    字典的两种取值的区别 (objectForKey: 和 valueForKey )参考 一般来说 key 可以是任意字符串组合,如果 key 不是以 @ 符号开头,这时候 valueForKey: 等 ...

  3. HashMap,Hashset,ArrayList以及LinkedList集合的区别,以及各自的用法

    基础内容 容器就是一种装其他各种对象的器皿.java.util包 容器:Set, List, Map ,数组.只有这四种容器. Collection(集合) 一个一个往里装,Map 一对一对往里装. ...

  4. Python中列表,元组,字典,集合的区别

    参考文档https://blog.csdn.net/Yeoman92/article/details/56289287 理解Python中列表,元组,字典,集合的区别 列表,元组,字典,集合的区别是p ...

  5. js实现网页防止被iframe框架嵌套及几种location.href的区别

    首先我们了解一下几种location.href的区别简单的说:几种location.href的区别js实现网页被iframe框架功能,感兴趣的朋友可以了解下 首先我们了解一下:window.locat ...

  6. 对于String对象,可以使用"="赋值,也可以使用"new"关键字赋值,两种方式有什么区别?

    当你看见这个标题的时候,你可能会下意识的去想一下,这两种方式到底有什么样的区别呢? 且看下面的demo,自然便区分开了 /** * */ package com.b510.test; /** * Pr ...

  7. C语言:几种字符输入函数的区别

    几种字符输入函数的区别: 1.getche()函数:用于从键盘读入一个字符并显示,然后直接执行下一条语   句. 2.getch()函数:用于从键盘中读入一个字符,但不显示在屏幕上,       然后 ...

  8. Java语言----三种循环语句的区别

    ------- android培训.java培训.期待与您交流! ---------- 第一种:for循环 循环结构for语句的格式:       for(初始化表达式;条件表达式;循环后的操作表达式 ...

  9. Android(java)学习笔记227:服务(service)之服务的生命周期 与 两种启动服务的区别

    1.之前我们在Android(java)学习笔记171:Service生命周期 (2015-08-18 10:56)说明过,可以回头看看: 2.Service 的两种启动方法和区别: (1)Servi ...

随机推荐

  1. go学习笔记-运算符

    运算符 运算符 内置运算符 算术运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 其他运算符 算术运算符 假定 A 值为 10,B 值为 20. 运算符 描述 实例 + 相加 A + B 输出结果 ...

  2. java web相对路径和绝对路径总结

    java web 开发过程中很多地方涉及url路径的问题,比如jsp页面.servlet之间的跳转.其实,可以将url中的/xxx看成一级目录,然后像看待目录层级之间的关系那样去看待url路径.接下来 ...

  3. REPLACE(替换字段内容)

    语法: REPLACE <str1> WITH <str2> INTO <c> [LENGTH <l> ]. ABAP/4 搜索字段 <c> ...

  4. 在List中删除符合条件的内容

    objDAList.RemoveAll(s => s.daCID == "20170725152407CD");

  5. 用 Qt 控制 Nikon 显微镜的电动物镜转盘

    用 Qt 控制 Nikon 显微镜的电动物镜转盘 最近的一个项目,用到了一台 Nikon 的金相显微镜,并且配了个电动的物镜转盘.为了控制这个电动物镜转盘,我折腾了差不多有4-5天.中间遇到了各种问题 ...

  6. 思杰VDI提示“The VDI is not available”

    前言:困扰已久的问题终于解决. 问题:客户反馈无法连接VDI. 解决过程:1.登录后台查看VDI状态为关机状态尝试重新启动提示如下图: 2.判断此VDI的启动盘出现问题(注:本人环境无数据盘) 3.查 ...

  7. Python-学习-项目1-即时标记-1

    买了一本Python入门,奈何看不下去,只能是先看后面的项目,看到那里不懂的时候在回去学习. 项目名字:即时标记 大致的意思就是把一个纯文本文件标记成自己想要的格式文件. 首先就是待处理文本,我找不到 ...

  8. [leetcode-644-Maximum Average Subarray II]

    Given an array consisting of n integers, find the contiguous subarray whose length is greater than o ...

  9. mongodb数据库操作之简单查询

    1. 2. 3.修改器 默认一条一条修改 4. 5.查询 6.mysql简单操作

  10. Python之tornado框架原理

    Python web框架 1.简单概念 tornado socket.逻辑处理 Django flask 逻辑处理 第三方处理模块(包含了socket) jinja2模块 Models 数据库处理 V ...