java集合的实现细节--ArrayList和LinkedList
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的更多相关文章
- java——集合、泛型、ArrayList、LinkedList、foreach循环、模拟ktv点歌系统
集合:一系列特殊的类,这些类可以存储任意类型的对象,长度可变,集合类都在java.util包中. 但是集合记不住对象的类型,当把对象从集合中取出时这个对象的编译类型就变成了Object类型.这样在取元 ...
- Java集合系列(二):ArrayList、LinkedList、Vector的使用方法及区别
本篇博客主要讲解List接口的三个实现类ArrayList.LinkedList.Vector的使用方法以及三者之间的区别. 1. ArrayList使用 ArrayList是List接口最常用的实现 ...
- java集合【12】——— ArrayList,LinkedList,Vector的相同点与区别是什么?
目录 特性列举 底层存储结构不同 线程安全性不同 默认的大小不同 扩容机制 迭代器 增删改查的效率 总结一下 要想回答这个问题,可以先把各种都讲特性,然后再从底层存储结构,线程安全,默认大小,扩容机制 ...
- java 集合之实现类ArrayList 和 LinkedList
List 的方法列表 方法名 功能说明 ArrayList() 构造方法,用于创建一个空的数组列表 add(E e) 将指定的元素添加到此列表的尾部 get(int index) 返回此列表中指定位置 ...
- java集合框架collection(2)ArrayList和LinkedList
ArrayList是基于动态数组实现的list,而LinkedList是基于链表实现的list.所以,ArrayList拥有着数组的特性,LinkedList拥有着链表的特性. 优缺点 ArrayLi ...
- Java 集合:List(ArrayList,LinkedList)
- Java 集合系列08之 List总结(LinkedList, ArrayList等使用场景和性能分析)
概要 前面,我们学完了List的全部内容(ArrayList, LinkedList, Vector, Stack). Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例 Ja ...
- 【转】Java 集合系列08之 List总结(LinkedList, ArrayList等使用场景和性能分析)
概要 前面,我们学完了List的全部内容(ArrayList, LinkedList, Vector, Stack). Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例 Ja ...
- java基础解析系列(十)---ArrayList和LinkedList源码及使用分析
java基础解析系列(十)---ArrayList和LinkedList源码及使用分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder jav ...
随机推荐
- 《开始使用Linux》单元测验 1
C语言编写的应用程序,通过printf打印一个换行符\n,但在终端上执行的是回车加换行\r\n,把换行符替换为回车换行是由下面哪个软件模块完成的? Linux内核中的行律模块 下面哪个命令可以获得ma ...
- HDFS 的垃圾回收配置
HDFS的垃圾回收 的默认配置的 0,也就是说,如果你不小心误删除了某样东西,那么这个操作是不可恢复的. 但是如果配置了HDFS的垃圾回收机制,那么删除的东西就可以在垃圾箱中保存一段你配置的时间,等 ...
- vs项目模板
创建项目模板 Creating a VSIX Deployable Project (or Item) Template with Custom Wizard Support Create a Pro ...
- 洛谷P1803 凌乱的yyy dp
我要日更嘤嘤嘤>_< 原题戳>>https://www.luogu.org/problem/show?pid=1803<<(其实是戳不动的,复制粘贴吧) 题目背景 ...
- MySQL获取指定长度的字符串的函数left(s,n)和right(s,n)
1.LEFT(s,n)函数返回字符串s开始的最左边n个字符. eg: select left(‘abcde12345‘,5); ======>‘abcde‘ 2.RIGHT(s,n)函数返回 ...
- 封装fetch的使用(包含超时处理)
// 1: 传统fetch操作 fetch('http://facebook.github.io/react-native/movies.json') .then((response) => r ...
- React组件导入的两种方式(动态导入组件的实现)
一. react组件两种导入方式 React组件可以通过两种方式导入另一个组件 import(常用) import component from './component' require const ...
- linux 校准时间方法
Debian.Ubuntu 系统安装NTP校时包: apt-get install ntpdate CentOS系统安装NTP校时包: yum install ntp 校时 ...
- 3.1 vue组件的使用
1. 组件 组件: 组件是一个局部功能界面,它包含了所有要实现这个功能界面的相关资源,如css.html等. 组件化编程: vue文件包含3个部分 <template> <div&g ...
- [JS]给String对象添加方法,使传入的字符串字符之间以空格分开输出
看到一个这样子的面试题: 给String对象添加一个方法,传入一个string类型的参数,然后将string的每一个字符间加空格返回,例如:addSpace("hello world&quo ...