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. MPU6050

    MPU6050: Pitch,Roll,Yaw旋转方向遵循右手定则 pith角  –绕Y轴(俯仰)  范围:±90°  ,与旋转方向相反转是增大   -- 抬头为正,低头为负 roll角  –绕X轴( ...

  2. JavaWeb--简单分页技术

    分页需要的技术点:1.前台分页标签的使用 2.前台上一页,下一页显示的业务逻辑 3.MSQL用到的语句  limit 4.封装pageBean对象 这个是PageBean用到的 分页公式: int t ...

  3. PowerDesigner使用方法入门学习

    [转载:https://www.cnblogs.com/biehongli/p/6025954.html] 1:入门级使用PowerDesigner软件创建数据库(直接上图怎么创建,其他的概念知识可自 ...

  4. FZU 2150 Fire Game(点火游戏)

    FZU 2150 Fire Game(点火游戏) Time Limit: 1000 mSec    Memory Limit : 32768 KB Problem Description - 题目描述 ...

  5. 51nod 1422 沙拉酱前缀

    http://www.51nod.com/onlineJudge/questionCode.html#problemId=1422&noticeId=399940 题意: 思路: 先把所有步骤 ...

  6. coercing to Unicode: need string or buffer, int found报错

    转为string类型 str(a)

  7. go 单进程并发

    demo1 // This sample program demonstrates how to create goroutines and // how the scheduler behaves. ...

  8. vim的简单使用

    vim的学习曲线相当的大(参看各种文本编辑器的学习曲线),所以,如果你一开始看到的是一大堆VIM的命令分类,你一定会对这个编辑器失去兴趣的.下面的文章翻译自<Learn Vim Progress ...

  9. SpringBoot获取全局配置文件的属性以及@ConfigurationProperties实现类型安全的配置

    在SpringBoot,可以定义一个全局配置文件,全局配置文件有两种形式: 1). application.properties 2).application.yml 二者的后缀名不同,编辑的格式也不 ...

  10. Java获得日期相差的天数

    文章来源: http://www.jb51.net/article/75551.htm 这篇文章主要介绍了java计算两个时间相差天数的方法,感兴趣的小伙伴们可以参考一下: 问题描述: 输入:两个日期 ...