ArrayList和LinkedList的实现差异

  List代表一种线性表的数据结构,ArrayList则是一种顺序存储的线性表,ArrayList底层采用动态数组的形式保存每一个集合元素,LinkedList则是一种链式存储的线性表,其本质上就是一个双向链表,它不仅实现了List接口,还实现了Deque接口,Deque代表了一种双端队列,既具有队列(FIFO)的特性,也具有栈(FILO)的特性,也就是说,LinkedList既可以当成双向链表使用,也可以当成队列使用,还可以当成栈来使用。

public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable

  ArrayList底层采用一个elementData数组来保存所有集合元素,因此ArrayList在插入元素时需要完成两件事情:

  ①、保证ArrayList底层封装的数组长度大于集合元素的个数

  ②、将插入位置之后的所有数组元素“整体搬家”,向后移动一“格”

  当删除ArrayList集合中指定位置的元素时,程序也要进行“整体搬家”,而且还需要将被删除索引处的数组元素赋值为null,下面是ArrayList集合的remove(int index)方法的源代码。

public E remove(int index) {
rangeCheck(index); modCount++;
E oldValue = elementData(index); int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work return oldValue;
}

  对于ArrayList集合而言,当程序向ArrayList中添加、删除集合元素时,ArrayList底层都是需要对数组进行“整体搬家”,因此性能很差。

  但如果程序调用get(int index)方法取出ArrayList集合中的元素时,性能和数组几乎相同,效率非常快。下面是ArrayList集合的get(int index)方法的源代码。

  E elementData(int index) {
return (E) elementData[index];
} public E get(int index) {
rangeCheck(index);
return elementData(index);
}

  LinkedList本质上是一个双向链表,因此它使用内部类来保存每一个集合元素。

 private static class Node<E> {
     //集合元素
E item;
     //保存指向下一个链表节点的引用
Node<E> next;
     //保存指向上一个链表节点的引用
Node<E> prev;
     //普通构造器
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}

  从上面程序中的粗体字代码可以看出,一个Node对象代表双向链表的一个节点,该对象中的next变量指向下一个节点,previous子指向上一个节点。

  由于LinkedList采用双向链表来保存集合元素,因此它在添加集合元素时,只要对链表进行插入即可。下面是LinkedList添加节点的源码。

     public void add(int index, E element) {
checkPositionIndex(index); if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}

  从上面代码看出,由于Linked本质上就是一个双向链表,因此它非常方便的在指定节点之前插入新节点。

  上面add(int index,E element)方法实现中用到了以下三个方法。

  ①、node(int index):搜索指定索引出的元素

  ②、linkLast(E e):将e新节点插入到最后

  ③、linkBefore(E e,Node<E> succ):在succ节点之前插入e新节点

  node(int index)实际上就是get(int index)方法的底层实现。Linked必须逐个元素的搜索,直到找到第index个元素为止。node(int index)采用二分法的方式进行查找,以下是该方法的源代码。

   Node<E> node(int index) {
// assert isElementIndex(index);
     //如果index小于size/2
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}

  上面的node(int index)方法就是逐个元素的找到index索引处的元素,只是由于LinkedList是一个双向链表,因此程序先根据index的值判断它离链表头近还是离链表尾近,如果离链表头近则从头端开始搜索,如果离链表尾近则从尾端开始搜素。

  Linked的get(int index)方法只是对上面node(int index)方法的简单包装。get(int index)方法的源代码如下。

    public E get(int index) {
checkElementIndex(index);
return node(index).item;
}

  但单纯的插入操作就比较简单了,只要修改几个节点里面的previous、next引用的值即可。下面是linkedbefore(E e, Node<E> succ)方法的源代码。

   void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}

  如果只是单纯的添加某个节点,那么LinkedList的性能是非常好的,但如果需要向某个指定索引处添加节点,LinkedList必须先找到指定索引处的节点,这个搜索过程的系统开销并不小,因此LinkedList的add(int index,E e)方法的性能并不是特别好。如果只使用LinkedList的addFrist(E e)、addLast(E e)、offerFrist(E e)、offerLast(E e)、pollFrist(E e)、pollLast(E e)的话,性能是非常好的,因为可以避免搜索过程。

  类似的,LinkedList为了实现remove(int index)方法,也必须先通过node(int index)方法找到index索引处的节点,然后修改它前一个节点的next引用,以及后一个节点的previous引用,下面是该方法的源代码。

    public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
} E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev; if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
} if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
} x.item = null;
size--;
modCount++;
return element;
}

  综上所述,ArrayList、LinkedList有各自适用的场景,大部分情况下,ArrayList的性能总是优于Linkedlist,因此绝大部分都应该考虑使用ArrayList集合。但如果程序荆楚需要添加、删除元素,尤其是经常需要add(E e)方法向集合中添加元素,则应该考虑使用LinkedList集合了。

java集合的实现细节--ArrayList和LinkedList的更多相关文章

  1. java——集合、泛型、ArrayList、LinkedList、foreach循环、模拟ktv点歌系统

    集合:一系列特殊的类,这些类可以存储任意类型的对象,长度可变,集合类都在java.util包中. 但是集合记不住对象的类型,当把对象从集合中取出时这个对象的编译类型就变成了Object类型.这样在取元 ...

  2. Java集合系列(二):ArrayList、LinkedList、Vector的使用方法及区别

    本篇博客主要讲解List接口的三个实现类ArrayList.LinkedList.Vector的使用方法以及三者之间的区别. 1. ArrayList使用 ArrayList是List接口最常用的实现 ...

  3. java集合【12】——— ArrayList,LinkedList,Vector的相同点与区别是什么?

    目录 特性列举 底层存储结构不同 线程安全性不同 默认的大小不同 扩容机制 迭代器 增删改查的效率 总结一下 要想回答这个问题,可以先把各种都讲特性,然后再从底层存储结构,线程安全,默认大小,扩容机制 ...

  4. java 集合之实现类ArrayList 和 LinkedList

    List 的方法列表 方法名 功能说明 ArrayList() 构造方法,用于创建一个空的数组列表 add(E e) 将指定的元素添加到此列表的尾部 get(int index) 返回此列表中指定位置 ...

  5. java集合框架collection(2)ArrayList和LinkedList

    ArrayList是基于动态数组实现的list,而LinkedList是基于链表实现的list.所以,ArrayList拥有着数组的特性,LinkedList拥有着链表的特性. 优缺点 ArrayLi ...

  6. Java 集合:List(ArrayList,LinkedList)

  7. Java 集合系列08之 List总结(LinkedList, ArrayList等使用场景和性能分析)

    概要 前面,我们学完了List的全部内容(ArrayList, LinkedList, Vector, Stack). Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例 Ja ...

  8. 【转】Java 集合系列08之 List总结(LinkedList, ArrayList等使用场景和性能分析)

    概要 前面,我们学完了List的全部内容(ArrayList, LinkedList, Vector, Stack). Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例 Ja ...

  9. java基础解析系列(十)---ArrayList和LinkedList源码及使用分析

    java基础解析系列(十)---ArrayList和LinkedList源码及使用分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder jav ...

随机推荐

  1. 【Hadoop 分布式部署 五:分布式部署之分发、基本测试及监控】

    1.对  hadoop 进行格式化 到  /opt/app/hadoop-2.5.0  目录下 执行命令:    bin/hdfs namenode -format 执行的效果图如下  (  下图成功 ...

  2. Kubernetes命令

    kubectl applykubectl getkubectl set image deployment/xxx -n ns  echoservice=xxxkubectl deletekubectl ...

  3. 8、nginx和tengine简介

    练习: 使用nginx反向代理(rr调度)用户请求至两个以上的后端LAMP(按标准路径部署的有pma,wd),不管用户请求是什么内容都反向代理至后端服务器去,但是如果用户请求的是图片或者是html,就 ...

  4. web前端知识总结

    前言: 一直想着整理一下关于前端的知识体系和资料,工作忙了些,挤挤总会有的,资料很多,就看你能不能耐下心坚持去学了,要多学多敲多想,祝你进步~ 学习之前首先要大概了解什么是HTML ,CSS , JS ...

  5. python学习 day06打卡

    今天学习的主要内容是: 一,小数据池 代码块的概念 python程序是由代码块构成的,一个代码块的文本作为python程序执行的单元. 代码块:一个模块,一个函数,一个类,甚至每一个command命令 ...

  6. git介绍和常用命令总结

    git中经常用的命令就是以下六个: 以下是命令总结: 另外,自己碰到的问题及解决方法: 在分支内提交远程仓库,-am: revert后进入vim,一直按住esc ,再连续按大写的z两次就退出来了: g ...

  7. 【Ruby】【高级编程】面向对象

    # [[面向对象]]#[实例变量]=begin实例变量是类属性,它们在使用类创建对象时就编程对象的属性.每个对象的属性是单独赋值的,和其他对象之间不共享.在类的内部,使用@运算符访问这些属性,在类的外 ...

  8. SpringBoot整合+logback日志配置

    本次演示的代码结构如下,基于maven,整合SpringBoot.Spring.Mybaits的SSM框架.同时测试logback日志框架的使用及配置. 1.创建maven工程,修改pom.xml文件 ...

  9. leecode第五十九题(螺旋矩阵 II)

    class Solution { public: vector<vector<int>> generateMatrix(int n) { )//特殊情况 { vector< ...

  10. PySpark笔记

    spark源码位置:https://github.com/apache/spark Spark Core核心RDD及编程 什么是RDD:1.是一个抽象类不能直接使用,在子类中实现抽象方法是一个抽象类不 ...