public static void main(String[] args){
final int MAX_VAL = 10000;
List<Integer> linkedList = new LinkedList<Integer>();
List<Integer> arrayList = new ArrayList<Integer>();
for(int i = 0; i < MAX_VAL; i++) {
linkedList.add(i);
arrayList.add(i);
}
long time = System.nanoTime();
for(int i = 0; i < MAX_VAL; i++) {
linkedList.add(MAX_VAL/2, i);
}
System.out.println("LL time: " + (System.nanoTime() - time));
time = System.nanoTime();
for(int i = 0; i < MAX_VAL; i++) {
arrayList.add(MAX_VAL/2, i);
}
System.out.println("AL time: " + (System.nanoTime() - time));
}

从中间插入结果:

怎么会这样, 不应该是LinkedList更快吗? ArrayList底层是数组, 添加数据需要移动后面的数据, 而LinkedList使用的是链表, 直接移动指针就行, 按理说应该是LinkedList更快.

再来看

从尾插入

public static void main(String[] args){
final int MAX_VAL = 10000;
List<Integer> linkedList = new LinkedList<Integer>();
List<Integer> arrayList = new ArrayList<Integer>();
for(int i = 0; i < MAX_VAL; i++) {
linkedList.add(i);
arrayList.add(i);
}
long time = System.nanoTime();
for(int i = 0; i < MAX_VAL; i++) {
linkedList.add(i);
}
System.out.println("LL time: " + (System.nanoTime() - time));
time = System.nanoTime();
for(int i = 0; i < MAX_VAL; i++) {
arrayList.add(i);
}
System.out.println("AL time: " + (System.nanoTime() - time));
}

从头开始插入

public static void main(String[] args){
final int MAX_VAL = 10000;
List<Integer> linkedList = new LinkedList<Integer>();
List<Integer> arrayList = new ArrayList<Integer>();
for(int i = 0; i < MAX_VAL; i++) {
linkedList.add(i);
arrayList.add(i);
}
long time = System.nanoTime();
for(int i = 0; i < MAX_VAL; i++) {
linkedList.add(0,i);
}
System.out.println("LL time: " + (System.nanoTime() - time));
time = System.nanoTime();
for(int i = 0; i < MAX_VAL; i++) {
arrayList.add(0,i);
}
System.out.println("AL time: " + (System.nanoTime() - time));
}

结果

然后从三分之一的位置开始插入

结果

从三分之二的位置插入

结果

源码部分

LinkedList源码

// 在index前添加节点,且节点的值为element
public void add(int index, E element) {
addBefore(element, (index==size ? header : entry(index)));
} // 获取双向链表中指定位置的节点
private Entry<E> entry(int index) {
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
Entry<E> e = header;
// 获取index处的节点。
// 若index < 双向链表长度的1/2,则从前向后查找;
// 否则,从后向前查找。
if (index < (size >> 1)) {
for (int i = 0; i <= index; i++)
e = e.next;
} else {
for (int i = size; i > index; i--)
e = e.previous;
}
return e;
} // 将节点(节点数据是e)添加到entry节点之前。
private Entry<E> addBefore(E e, Entry<E> entry) {
// 新建节点newEntry,将newEntry插入到节点e之前;并且设置newEntry的数据是e
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
// 插入newEntry到链表中
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;

从中,我们可以看出:通过add(int index, E element)向LinkedList插入元素时。先是在双向链表中找到要插入节点的位置index;找到之后,再插入一个新节点
双向链表查找index位置的节点时,有一个加速动作若index < 双向链表长度的1/2,则从前向后查找; 否则,从后向前查找

接着,我们看看ArrayList.java中向指定位置插入元素的代码。如下:

// 将e添加到ArrayList的指定位置
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size); ensureCapacity(size+1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}

ensureCapacity(size+1) 的作用是“确认ArrayList的容量,若容量不够,则增加容量。
真正耗时的操作是 System.arraycopy(elementData, index, elementData, index + 1, size - index);

实际上,我们只需要了解: System.arraycopy(elementData, index, elementData, index + 1, size - index); 会移动index之后所有元素即可这就意味着,ArrayList的add(int index, E element)函数,会引起index之后所有元素的改变!

结论:

现在大概知道了,插入位置的选取对LinkedList有很大的影响,一直往数据中间部分插入删除的时候,ArrayList比LinkedList更快

原因大概就是当数据量大的时候,system.arraycopy的效率要比每次插入LinkedList都需要从端查找index和分配节点node来的更快。

总之,对于99%或更多的现实情况,ArrayList是更好的选择,并且利用LinkedList的狭隘优势需要非常小心。

参考:https://stackoverflow.com/questions/16808777/is-linkedlist-really-faster-than-arraylist-in-the-case-of-insertion-in-the-middl

什么情况下ArrayList增删 比LinkedList 更快的更多相关文章

  1. java.util.ArrayList,java.util.LinkedList,java.util.Vector的区别,使用场合.

    下图是Collection的类继承图 从图中可以看出:Vector.ArrayList.LinkedList这三者都实现了List 接口.所有使用方式也很相似,主要区别在于实现方式的不同,所以对不同的 ...

  2. java里的数组和list分别在什么情况下使用?

    数组长度固定,List未限定长度,且支持的功能更多,最常用的ArrayList底层实际上也是使用数组实现. 不需要复杂功能和确定长度的情况下,使用数组效率更高,通常情况建议使用List.

  3. 什么情况用ArrayList or LinkedList呢?

    ArrayList 和 LinkedList 是 Java 集合框架中用来存储对象引用列表的两个类.ArrayList 和 LinkedList 都实现 List 接口.先对List做一个简单的了解: ...

  4. 使用QFileInfo类获取文件信息(在NTFS文件系统上,出于性能考虑,文件的所有权和权限检查在默认情况下是被禁用的,通过qt_ntfs_permission_lookup开启和操作。absolutePath()必须查询文件系统。而path()函数,可以直接作用于文件名本身,所以,path() 函数的运行会更快)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/Amnes1a/article/details/65444966QFileInfo类为我们提供了系统无 ...

  5. OC JS交互(通常情况下,如果网页写得正规的话,是可以正常交互的,之前就遇到后台写h5始终拿不到事件,元素也拿不到,更别说交互了,真是奇了怪了)

    自动填充表单 @"document.getElementById('loginid').value = '这里填入你的用户名';document.getElementById('userpa ...

  6. Java高并发情况下的锁机制优化

    本文主要讲并行优化的几种方式, 其结构如下: 锁优化 减少锁的持有时间 例如避免给整个方法加锁 1 public synchronized void syncMethod(){ 2 othercode ...

  7. 如何彻底理解Java抽象类 为什么要用抽象类 什么情况下用抽象类

    如何彻底理解Java抽象类 为什么要用抽象类 什么情况下用抽象类 呐,到底什么是抽象类,有时明明一个普通类就可以解决了,为啥非得整个抽象类,装逼吗 我曾带着这样的疑惑,查了很多资料,看了很多源码,写了 ...

  8. Redis面试题记录--缓存双写情况下导致数据不一致问题

    转载自:https://blog.csdn.net/lzhcoder/article/details/79469123 https://blog.csdn.net/u013374645/article ...

  9. 用SignalR 2.0开发客服系统[系列4:负载均衡的情况下使用SignalR]

    前言 交流群:195866844 目录: 用SignalR 2.0开发客服系统[系列1:实现群发通讯] 用SignalR 2.0开发客服系统[系列2:实现聊天室] 用SignalR 2.0开发客服系统 ...

随机推荐

  1. AngularJS实现文件异步上传

    实现:前端AngularJs+后端servelt 依赖包:commons-fileupload.x.x.jar commons-io-x.x.jar 下载: http://commons.apache ...

  2. 20155205 《Java程序设计》实验三(敏捷开发与XP实践)实验报告

    20155205 <Java程序设计>实验三(敏捷开发与XP实践)实验报告 一.实验内容及步骤 (一)使用Code菜单 在IDEA中使用工具(Code->Reformate Code ...

  3. 利用tcpcopy引流过程

    tcpcopy是一个tcp流量复制工具,当前还支持udp和mysql流量的复制. 目的: 将机器10.24.110.21的5000端口流量引流到机器10.23.25.11的5000端口. 示例:将10 ...

  4. SSM_CRUD新手练习(2)配置文件

    配置之前现需要引入依赖的jar包: *Spring *SpringMvc *Mybatis *数据库连接池,驱动包 *其他(jstl,Servlet ,Junit..) 1.poxm.xml < ...

  5. ETL化的IOTA架构

    经过这么多年的发展,已经从大数据1.0的BI/Datawarehouse时代,经过大数据2.0的Web/APP过渡,进入到了IOT的大数据3.0时代,而随之而来的是数据架构的变化. ▌Lambda架构 ...

  6. HDU2732一个让我debug了一晚上的题目

    思路都理解了,清晰了,就是代码不对,还是有些小地方自己注意不到,即使就在你的眼前也不易发现的那种 Description: 也是一个最大流的构图,没相出来,或者说想简单了也是标记点1 至 n * m是 ...

  7. ASP.NET MVC ActionMethodSelectorAttribute 以及HttpGet等Action特性

    一.ActionMethodSelectorAttribute 其是一个抽象类,继承自Attribute,子类有NonActionAttribute.HttpGetAttribute.HttpPost ...

  8. R12 AR INVOICE 接口表导入

    http://blog.csdn.net/fangz0615/article/details/38677085 Purpose 本文介绍了如何通过AR接口表进行AR事务处理(亦称AR发票)导入. Ap ...

  9. Delphi 在DLL中使用DevExpress控件时出错解决办法

    测试环境 DevExpress VCL 14.1.3 和XE7 问题:在dll使用cxGrid控件时  如果不添加列标题 则不报错   查询无数据集显示,如果加上标题 就报错了 这段为报错部分 fun ...

  10. IO模型 IO多路复用

    阻塞IO 用socket 一定会用到accept recv recvfrom这些方法正常情况下 accept recv recvfrom都是阻塞的 非阻塞IO 如果setblocking(False) ...