博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
LinkedList源码浅析
阅读量:4060 次
发布时间:2019-05-25

本文共 7483 字,大约阅读时间需要 24 分钟。

LinkedList基于双端链表实现;对元素的定位由秩(rank,index)到位置(position);

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

可以认为:LinkedList中对方法的定义是由List,Deque(读音同deck)定义的,而方法的实现是由AbstractSequentialList定义的(源码见文章末尾);

节点类 Node

一个节点由前驱节点,后继节点和此节点指向的元素组成;

private static class Node
{
E item; Node
next; Node
prev; Node(Node
prev, E element, Node
next) {
this.item = element; this.next = next; this.prev = prev; } }

add

public boolean add(E e) {
linkLast(e); return true; } /** * Links e as last element. */ void linkLast(E e) {
final Node
l = last;//指向链表尾部 final Node
newNode = new Node<>(l, e, null);//以尾部为前驱节点创建一个新节点 last = newNode;//将链表尾部指向新节点 if (l == null)//如果链表为空,那么该节点既是头节点也是尾节点 first = newNode; else//链表不为空,那么将该结点作为原链表尾部的后继节点 l.next = newNode; size++;//size加一 modCount++;//fail-fast 机制

可见每次插入都是移动指针,和 ArrayList 的拷贝数组来说效率要高上不少。

add(int index,E e)

public void add(int index, E element) {
checkPositionIndex(index); //检查索引是否处于[0-size]之间 if (index == size)//添加在链表尾部 linkLast(element); else//添加在链表中间 linkBefore(element, node(index)); }
  • 校验index的范围是否合法
  • 如果插入位置是链表尾部,那么调用linkLast方法
  • 如果插入位置是链表中间,那么调用linkBefore方法

remove

remove分为removeFirst()和removeLast();两者类似;以removeLast为例看一下其源码:

public E removeLast() {
final Node
l = last; if (l == null) throw new NoSuchElementException(); return unlinkLast(l); }
/**    * Unlinks non-null last node l.    */   private E unlinkLast(Node
l) {
// assert l == last && l != null; final E element = l.item;//节点所指向元素 final Node
prev = l.prev;//节点前驱节点 l.item = null;//将节点所指向元素和与前驱节点的关联置空,便于GC l.prev = null; // help GC last = prev;//前驱节点定义为新的末节点last if (prev == null) first = null;//前驱节点为空;则首节点first定义为null,此时列表为空 else prev.next = null;//前驱节点存在的话,将其后继设为null size--;//size-1 modCount++; return element; }

set 修改

public E set(int index, E element) {
checkElementIndex(index); Node
x = node(index); E oldVal = x.item; x.item = element; return oldVal; }
  • 基于双端列表实现的linkedList的修改也十分方便;只需要修改目标index节点指向的元素对象即可;不需要修改其前驱和后继.

get ***

public E get(int index) {
checkElementIndex(index); return node(index).item; } Node
node(int index) {
// assert isElementIndex(index); if (index < (size >> 1)) {
//index位于列表的前半部分,则从前往后遍历 Node
x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else {
Node
x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }
  • 上述代码,利用了双向链表的特性,如果index离链表头比较近,就从节点头部遍历。否则就从节点尾部开始遍历。使用空间(双向链表)来换取时间。
  • LinkedList集合可以根据头尾进行各种操作,但它的增删效率高O(1),查询效率低O(n);

AbstractSequentialList源码:

package java.util;public abstract class AbstractSequentialList
extends AbstractList
{
protected AbstractSequentialList() {
} public E get(int index) {
try {
return listIterator(index).next(); } catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index); } } public E set(int index, E element) {
try {
ListIterator
e = listIterator(index); E oldVal = e.next(); e.set(element); return oldVal; } catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index); } } public void add(int index, E element) {
try {
listIterator(index).add(element); } catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index); } } public E remove(int index) {
try {
ListIterator
e = listIterator(index); E outCast = e.next(); e.remove(); return outCast; } catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index); } } public boolean addAll(int index, Collection
c) {
try {
boolean modified = false; ListIterator
e1 = listIterator(index); Iterator
e2 = c.iterator(); while (e2.hasNext()) {
e1.add(e2.next()); modified = true; } return modified; } catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index); } } public Iterator
iterator() { return listIterator(); } public abstract ListIterator
listIterator(int index);}

官方注释

允许元素为null

Doubly-linked list implementation of the List and Deque interfaces. Implements all optional list operations, and permits all elements (including null).

All of the operations perform as could be expected for a doubly-linked list. Operations that index into the list will traverse the list from the beginning or the end, whichever is closer to the specified index.

Note that this implementation is not synchronized.不是线程安全的;可以使用如下方式转换成线程安全的list:

List list = Collections.synchronizedList(new LinkedList(...));

Note that this implementation is not synchronized. If multiple threads access a linked list concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements; merely setting the value of an element is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the list. If no such object exists, the list should be “wrapped” using the Collections.synchronizedList method. This is best done at creation time, to prevent accidental unsynchronized access to the list:

List list = Collections.synchronizedList(new LinkedList(…));

fail-fast

The iterators returned by this class’s iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the Iterator’s own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

转载地址:http://llwji.baihongyu.com/

你可能感兴趣的文章
9、VUE面经
查看>>
关于进制转换的具体实现代码
查看>>
Golang 数据可视化利器 go-echarts ,实际使用
查看>>
mysql 跨机器查询,使用dblink
查看>>
mysql5.6.34 升级到mysql5.7.32
查看>>
dba 常用查询
查看>>
Oracle 异机恢复
查看>>
Oracle 12C DG 搭建(RAC-RAC/RAC-单机)
查看>>
Truncate 表之恢复
查看>>
Oracle DG failover 后恢复
查看>>
mysql 主从同步配置
查看>>
为什么很多程序员都选择跳槽?
查看>>
mongdb介绍
查看>>
mongdb在java中的应用
查看>>
区块链技术让Yotta企业云盘为行政事业服务助力
查看>>
Yotta企业云盘更好的为媒体广告业服务
查看>>
Yotta企业云盘助力科技行业创高峰
查看>>
Yotta企业云盘更好地为教育行业服务
查看>>
Yotta企业云盘怎么帮助到能源化工行业
查看>>
企业云盘如何助力商业新发展
查看>>