LinkedList源代码阅读

LinkedList源代码阅读

1.1LinkedList介绍

LinkedList 是 Java 中常用的一个数据结构,它是一种基于链表的实现,提供了一些方法来添加、删除、获取节点等操作。
LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
LinkedList 实现 List 接口,能对它进行队列操作。
LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
LinkedList 是非同步的

1.2 LinkedList源码

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    // 元素个数
    transient int size = 0;

    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    // 链表首节点
    transient Node<E> first;

    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    // 链表尾节点
    transient Node<E> last;


	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;
        }
    }


在上面的代码中,LinkedList 继承了AbstractSequentialList,实现了 List、Deque、Cloneable 和 java.io.Serializable 接口,定义了 size、first、last 等属性。

其中,size 属性用于记录链表当前存储的元素数量,first 和 last 分别表示链表的首节点和尾节点。在节点方面,LinkedList 定义了一个内部类 Node,用于储存链表中的元素信息以及节点信息。

Node 类定义了三个属性:item(用于储存元素信息)、next(用于指向下一个节点)和 prev(用于指向上一个节点)。在构造函数中,我们需要传入上一个节点、当前元素以及下一个节点的信息,从而创建一个新的节点。

除此之外,LinkedList 还提供了一些方法,例如 add()、remove()、get() 等方法,这些方法的具体实现都涉及到底层的双向链表操作。

1.2.1 主要构造方法
(1).LinkedList()
public LinkedList() {
}

LinkedList 的无参构造就是构造一个空的list集合

(2) LinkedList(Collection<? extends E> c)
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。

1.2.2 主要内部类

典型的双链表结构

private static class Node<E> {
    // item表示当前存储元素
    E item;
    // next表示当前节点的后置节点
    Node<E> next;
    // prev表示当前节点的前置节点
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

LinkedList 是通过双向链表实现的,而双向链表就是通过Node类来实现的,Node类中通过item变量存储当前元素,通过next变量指向当前节点的下一个节点,通过prev变量指向当前节点的上一个节点。

1.2.3 LinkedList增加节点
(1). add(E e)
public boolean add(E e) {
    //在末尾添加一个元素
    linkLast(e);
    return true;
}

void linkLast(E e) {
    // 获取链表的最后一个节点
    final Node<E> l = last;
    // 创建一个新节点
    final Node<E> newNode = new Node<>(l, e, null);
     // 使新的一个节点为最后一个节点
    last = newNode;
     // 如果最后一个节点为null,则表示链表为空,则将newNode赋值给first节点
    if (l == null)
        first = newNode;
    // 否则尾节点的last指向 newNode
    else
        l.next = newNode;
    // 元素的个数加1
    size++;
    // 结构修改的次数自增+1
    modCount++;
}
  • 第一步,获取链表的最后一个节点
  • 第二步,创建一个新节点
  • 第三步,使新的一个节点为最后一个节点
  • 第四步,如果最后一个节点为null,则表示链表为空,则将newNode赋值给first节点;否则尾节点的last指向 newNode
(2). add(int index, E element)

在指定位置插入元素

public void add(int index, E element) {
    // 检查索引index的位置
    checkPositionIndex(index);
	// 如果index是在队列尾节点之后的一个位置
    // 把新节点直接添加到尾节点之后
    // 否则调用linkBefore()方法在中间添加节点
    if (index == size)
        linkLast(element);
    else
        linkBefore(element, node(index));
}

private void checkPositionIndex(int index) {
    if (!isPositionIndex(index))
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

private boolean isPositionIndex(int index) {
    return index >= 0 && index <= size;
}


void linkLast(E e) {
    // 获取链表的最后一个节点
    final Node<E> l = last;
    // 创建一个新节点
    final Node<E> newNode = new Node<>(l, e, null);
     // 使新的一个节点为最后一个节点
    last = newNode;
     // 如果最后一个节点为null,则表示链表为空,则将newNode赋值给first节点
    if (l == null)
        first = newNode;
    // 否则尾节点的last指向 newNode
    else
        l.next = newNode;
    // 元素的个数加1
    size++;
    // 结构修改的次数自增+1
    modCount++;
}


void linkBefore(E e, Node<E> succ) {
    // assert succ != null;
    // succ是待添加节点的后继节点
    // 找到待添加节点的前置节点
    final Node<E> pred = succ.prev;
    // 在其前置节点和后继节点之间创建一个新节点
    final Node<E> newNode = new Node<>(pred, e, succ);
    // 构建双向链表,succ的前驱节点为新的节点
    succ.prev = newNode;
    // 如果前置驱节点为null,则把newNode赋值给first
    if (pred == null)
        first = newNode;
    else
        // 构建双向列表
        pred.next = newNode;
    // 元素的个数加1
    size++;
    // 结构修改的次数自增+1
    modCount++;
}

// 寻找index位置的节点
Node<E> node(int index) {
    // 因为是双链表
    // 所以根据index是在前半段还是后半段决定从前遍历还是从后遍历
    // 这样index在后半段的时候可以少遍历一半的元素
    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;
    }
}
(3). addAll(Collection<? extends E> c)
public boolean addAll(Collection<? extends E> c) {
    // 在链表末尾添加一个集合
    return addAll(size, c);
}
(4).addAll(int index, Collection<? extends E> c)

在指定位置添加一个集合

public boolean addAll(int index, Collection<? extends E> c) {
    //检查索引是否越界
    checkPositionIndex(index);
	// 将集合转换成一个数组
    Object[] a = c.toArray();
    int numNew = a.length;
    // 如果集合为空,添加失败,返回false
    if (numNew == 0)
        return false;
   // 声明Node<E>类型变量pred(元素直接前驱), succ(元素直接后继)
    Node<E> pred, succ;
    // 如果给定下标等于链表原有元素个数
    if (index == size) {
        succ = null; // 设置给定尾集合元素直接后继为null
        pred = last; // 设置给定集合头元素直接前驱为last(原链表尾元素)
    } else {
        succ = node(index); // 给定集合尾元素直接后继为node(index)
        pred = succ.prev;   // 给定集合头元素直接前驱为succ.prev(确定上一个链表元素pred)
    }
	// 遍历集合元素,依次添加进链表
    for (Object o : a) {
        @SuppressWarnings("unchecked") E e = (E) o;
        // 添加尾元素
        Node<E> newNode = new Node<>(pred, e, null);
        // 如果是头元素,创建第一个元素
        if (pred == null)
            first = newNode;
        else
           	// 不为空则插入下一个元素
            pred.next = newNode;
        //为下一次循环做准备,设置元素直接前驱为newNode
        pred = newNode;
    }
	如果元素直接后继为null,说明这是尾元素 
    if (succ == null) {
        last = pred; //因上个循环最后将pred = newNode;	所以将最后一个元素赋值为尾元素
    } else {
       
        //赋值后置节点
        pred.next = succ;
        //赋值前置节点
        succ.prev = pred;
    }
	// 修改链表元素个数
    size += numNew;
    // 结构性改变次数自增
    modCount++;
    return true;
}
(5). addFirst(E e)

从队列首添加元素

public void addFirst(E e) {
    // 从队列首添加元素 
    linkFirst(e);
}

private void linkFirst(E e) {
    // 首节点
    final Node<E> f = first;
    // 创建新节点,新节点的next是首节点
    final Node<E> newNode = new Node<>(null, e, f);
    // 让新节点作为新的首节点
    first = newNode;
    // 判断是不是第一个添加的元素
    // 如果是就把last也置为新节点
    // 否则把原首节点的prev指针置为新节点
    if (f == null)
        last = newNode;
    else
        f.prev = newNode;
    // 元素个数加1
    size++;
    // 结构性改变次数自增
    modCount++;
}
(6). addLast(E e)

从队尾添加元素

public void addLast(E e) {
    linkLast(e);
}

void linkLast(E e) {
    // 获取链表的最后一个节点
    final Node<E> l = last;
    // 创建一个新节点
    final Node<E> newNode = new Node<>(l, e, null);
     // 使新的一个节点为最后一个节点
    last = newNode;
     // 如果最后一个节点为null,则表示链表为空,则将newNode赋值给first节点
    if (l == null)
        first = newNode;
    // 否则尾节点的last指向 newNode
    else
        l.next = newNode;
    // 元素的个数加1
    size++;
    // 结构修改的次数自增+1
    modCount++;
}
1.2.3 LinkedList删除节点
(1). remove()

从队首删除元素

public E remove() {
    return removeFirst();
}

public E removeFirst() {
    final Node<E> f = first;
    // 首节点为null抛出异常
    if (f == null)
        throw new NoSuchElementException();
    //从队首删除节点
    return unlinkFirst(f);
}

// 删除首节点
private E unlinkFirst(Node<E> f) {
    // assert f == first && f != null;
    // 首节点的元素值
    final E element = f.item;
    // 首节点的next指针
    final Node<E> next = f.next;
    // 设置首节点,首节点的next指针为null,协助GC
    f.item = null;
    f.next = null; // help GC
    // 把首节点的next作为新的首节点
    first = next;
    // 如果只有一个元素,删除了,把last也置为空
    // 否则把next的前置指针置为空
    if (next == null)
        last = null;
    else
        next.prev = null;
    // 链表元素个数自减-1
    size--;
    //结构性修改次数自增+1
    modCount++;
    // 返回删除的元素
    return element;
}
(2). remove(int index)

删除指定索引位置元素

public E remove(int index) {
    //检查索引是否越界
    checkElementIndex(index);
    //删除节点
    return unlink(node(index));
}
// 检查索引越界
private void checkElementIndex(int index) {
    if (!isElementIndex(index))
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

private boolean isElementIndex(int index) {
    return index >= 0 && index < size;
}

 // 寻找index位置的节点
Node<E> node(int index) {
    // assert isElementIndex(index);
	// 所以根据index是在前半段还是后半段决定从前遍历还是从后遍历
    // 这样index在后半段的时候可以少遍历一半的元素
    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;
    }
}


E unlink(Node<E> x) {
    // assert x != null;
    // x的元素值
    final E element = x.item;
    // x的前置节点
    final Node<E> next = x.next;
    // x的后置节点
    final Node<E> prev = x.prev;
	// 如果前置节点为空
    // 说明是首节点,让first指向x的后置节点
    // 否则修改前置节点的next为x的后置节点
    if (prev == null) {
        first = next;
    } else {
        prev.next = next;
        x.prev = null;
    }

    // 如果后置节点为空
    // 说明是尾节点,让last指向x的前置节点
    // 否则修改后置节点的prev为x的前置节点
    if (next == null) {
        last = prev;
    } else {
        next.prev = prev;
        x.next = null;
    }
 	// 清空x的元素值,协助GC
    x.item = null;
    // 元素个数减1
    size--;
    // 结构修改次数加1
    modCount++;
    // 返回删除的元素
    return element;
}
(3). remove(Object o)

根据元素删除

public boolean remove(Object o) {
    //如果元素为null,查找链表中元素值为null第一次出现的位置将其删除
    if (o == null) {
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
         //如果元素不为null,查找链表中元素值第一次出现的位置将其删除
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;
}
(4). removeFirst()

删除首节点

public E removeFirst() {
    // 判断首节点是否为空,为空则抛出异常
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return unlinkFirst(f);
}
// 删除首节点
private E unlinkFirst(Node<E> f) {
    // assert f == first && f != null;
    // 首节点的元素值
    final E element = f.item;
    // 首节点的next指针
    final Node<E> next = f.next;
    // 清空首节点的内容,协助GC
    f.item = null;
    f.next = null; // help GC
    // 把首节点的next作为新的首节点
    first = next;
    // 如果只有一个元素,删除了,把last也置为空
    // 否则把next的前置指针置为空
    if (next == null)
        last = null;
    else
        next.prev = null;
    // 元素个数减1
    size--;
    // 结构修改次数加1
    modCount++;
    // 返回删除的元素
    return element;
}
(5). removeLast()

删除尾结点

public E removeLast() {
    // 判断尾节点是否为空,为空则抛出异常
    final Node<E> l = last;
    if (l == null)
        throw new NoSuchElementException();
    // 删除尾结点
    return unlinkLast(l);
}

private E unlinkLast(Node<E> l) {
    // assert l == last && l != null;
    // 尾节点的元素值
    final E element = l.item;
    // 尾节点的上一指针
    final Node<E> prev = l.prev;
    //清空尾节点的内容,协助GC
    l.item = null;
    l.prev = null; // help GC
    // 让前置节点成为新的尾节点
    last = prev;
    // 如果只有一个元素,删除了把first置为空
    // 否则把前置节点的next置为空
    if (prev == null)
        first = null;
    else
        prev.next = null;
    // 元素个数减1
    size--;
    // 结构修改次数加1
    modCount++;
    // 返回删除的元素
    return element;
}
(6). removeFirstOccurrence(Object o)

删除指定元素第一次出现在该列表中(遍历从头部到尾部列表时)。如果列表中不包含该元素,它是不变的。

public boolean removeFirstOccurrence(Object o) {
    return remove(o);
}
(7).removeLastOccurrence(Object o)

删除此列表中最后一次出现的指定元素(从头到尾遍历列表时)。如果列表不包含该元素,则它保持不变。

public boolean removeLastOccurrence(Object o) {
    if (o == null) {
        for (Node<E> x = last; x != null; x = x.prev) {
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
        for (Node<E> x = last; x != null; x = x.prev) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;
}
1.2.4 LinkedList修改节点
(1) set(int index, E element)

根据索引修改节点

public E set(int index, E element) {
    //检查索引是否越界
    checkElementIndex(index);
    // 根据索引查找要修改的元素
    Node<E> x = node(index);
    // 去除被修改的元素
    E oldVal = x.item;
    // 将新元素赋值
    x.item = element;
    // 返回修改之前的元素
    return oldVal;
}
1.2.5 LinkedList查找节点
(1). get(int index)

根据索引查找节点

public E get(int index) {
    // 检查索引是否越界
    checkElementIndex(index);
    // 根据索引查找
    return node(index).item;
}


// 寻找index位置的节点
Node<E> node(int index) {
    // assert isElementIndex(index);
    // 所以根据index是在前半段还是后半段决定从前遍历还是从后遍历
    // 这样index在后半段的时候可以少遍历一半的元素
    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;
    }
}
(2). getFirst()/getLast()

获取首节点/尾结点

public E getFirst() {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return f.item;
}
public E getLast() {
     final Node<E> l = last;
     if (l == null)
        throw new NoSuchElementException();
    return l.item;
}

1.3 LinkedList作为栈使用

public void push(E e) {
    addFirst(e);
}

public E pop() {
    return removeFirst();
}

栈的特性是LIFO(Last In First Out),所以作为栈使用也很简单,添加删除元素都只操作队列首节点即可。

1.4 LinkedList总结

(1)LinkedList是一个以双链表实现的List;

(2)LinkedList还是一个双端队列,具有队列、双端队列、栈的特性;

(3)LinkedList在队列首尾添加、删除元素非常高效,时间复杂度为O(1);

(4)LinkedList在中间添加、删除元素比较低效,时间复杂度为O(n);

(5)LinkedList不支持随机访问,所以访问非队列首尾的元素比较低效;

(6)LinkedList在功能上等于ArrayList + ArrayDeque;

  • 31
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kⅈꫛᧁ269

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值