Java 集合-LinkedList

目录​​​​​​​

1.类声明

2.属性

3.构造方法

4.添加单个元素

5.添加元素到指定位置


1.类声明

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

如下 3 个接口是 ArrayList 一致的:

  • java.util.List 接口
  • java.io.Serializable 接口
  • java.lang.Cloneable 接口

如下 1 个接口是少于 ArrayList 的:

  • java.util.RandomAccess 接口,LinkedList 不同于 ArrayList 的很大一点,不支持随机访问。

如下 1 个接口是多于 ArrayList 的:

  • java.util.Deque 接口,提供双端队列的功能,LinkedList 支持快速的在头尾添加元素和读取元素,所以很容易实现该特性。

2.属性

 // 链表大小
    transient int size = 0;

    // 头节点
    transient Node first;

    // 尾节点
    transient Node last;

    // 节点
    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;
        }
    }

3.构造方法

    public LinkedList() {
    }

    public LinkedList(Collection c) {
        this();
    // 添加 c 到链表中
        addAll(c);
    }

相比 ArrayList 来说,因为没有容量一说,所以不需要提供 ArrayList(int initialCapacity) 这样的构造方法。

4.添加单个元素

    public boolean add(E e) {
        // 添加末尾
        linkLast(e);
        return true;
    }
    
    void linkLast(E e) {
        // 记录原 last 节点
        final Node l = last;
        // 创建新节点
        // 第一个参数表示,newNode 的前一个节点为 l
        // 第二个参数表示,e 为元素
        // 第三个参数表示,newNode 的后一个节点为 null
        final Node newNode = new Node<>(l, e, null);
        // last 指向新节点
        last = newNode;
        // 如果原 last 为 null ,说明 first 也为空,则 first 也指向新节点
        if (l == null)
            first = newNode;
        // 如果原 last 非 null ,说明 first 也非空,则原 last 的 next 指向新节点
        else
            l.next = newNode;
        // 增加链表大小
        size++;
        // 增加数组修改次数
        modCount++;
    }

5.添加元素到指定位置

public void add(int index, E element) {
    // 校验不要超过范围
    checkPositionIndex(index);

    // 如果刚好等于链表大小,直接添加到尾部
    if (index == size)
        linkLast(element);
    // 添加到第 index 的节点的前面
    else
        linkBefore(element, node(index));
}

void linkBefore(E e, Node succ) {
    // assert succ != null;
    // 获得 succ 的前一个节点
    final Node pred = succ.prev;
    // 创建新的节点 newNode
    final Node newNode = new Node<>(pred, e, succ);
    //  设置 succ 的前一个节点为新节点
    succ.prev = newNode;
    // 如果 pred 为 null ,说明 first 也为空,则 first 也指向新节点
    if (pred == null)
        first = newNode;
    // 如果 pred 非 null ,说明 first 也为空,则 pred 也指向新节点
    else
        pred.next = newNode;
    // 增加链表大小
    size++;
    // 增加数组修改次数
    modCount++;
}

因为 LinkedList 实现了 Deque 接口,所以它实现了 addFirst(E e) 和 addLast(E e) 方法,分别添加元素到链表的头尾

// LinkedList.java 实现 Deque 接口

public void addFirst(E e) {
    linkFirst(e);
}
public boolean offerFirst(E e) {
    addFirst(e); // 调用上面的方法
    return true;
}

public void addLast(E e) {
    linkLast(e);
}
public boolean offerLast(E e) {
    addLast(e); // 调用上面的方法
    return true;
}

addFirst(E e) 方法,调用 #linkFirst(E e) 方法,添加元素到队头。

private void linkFirst(E e) {
    // 记录原 first 节点
    final Node f = first;
    // 创建新节点
    final Node newNode = new Node<>(null, e, f);
    // first 指向新节点
    first = newNode;
    // 如果原 first 为空,说明 last 也为空,则 last 也指向新节点
    if (f == null)
        last = newNode;
    // 如果原 first 非空,说明 last 也非空,则原 first 的 next 指向新节点。
    else
        f.prev = newNode;
    // 增加链表大小
    size++;
    // 增加数组修改次数
    modCount++;
}

5.添加多个元素

public boolean addAll(Collection c) {
    return addAll(size, c);
}

public boolean addAll(int index, Collection c) {
    checkPositionIndex(index);

    // 将 c 转成 a 数组
    Object[] a = c.toArray();
    int numNew = a.length;
    if (numNew == 0) // 如果无添加元素,直接返回 false 数组未变更
        return false;

    // 获得第 index 位置的节点 succ ,和其前一个节点 pred
    Node pred, succ;
    if (index == size) { // 如果 index 就是链表大小,那说明插入队尾,所以 succ 为 null ,pred 为 last 。
        succ = null;
        pred = last;
    } else { // 如果 index 小于链表大小,则 succ 是第 index 个节点,prev 是 succ 的前一个二节点。
        succ = node(index);
        pred = succ.prev;
    }

    // 遍历 a 数组,添加到 pred 的后面
    for (Object o : a) {
        // 创建新节点
        @SuppressWarnings("unchecked") E e = (E) o;
        Node newNode = new Node<>(pred, e, null);
        // 如果 pred 为 null ,说明 first 也为 null ,则直接将 first 指向新节点
        if (pred == null)
            first = newNode;
        // pred 下一个指向新节点
        else
            pred.next = newNode;
        // 修改 pred 指向新节点
        pred = newNode;
    }

    // 修改 succ 和 pred 的指向
    if (succ == null) { // 如果 succ 为 null ,说明插入队尾,则直接修改 last 指向最后一个 pred
        last = pred;
    } else { // 如果 succ 非 null ,说明插入到 succ 的前面
        pred.next = succ; // prev 下一个指向 succ
        succ.prev = pred; // succes 前一个指向 pred
    }

    // 增加链表大小
    size += numNew;
    // 增加数组修改次数
    modCount++;
    // 返回 true 数组有变更
    return true;
}

6.移除单个元素

remove(int index) 方法,移除指定位置的元素,并返回该位置的原元素

public E remove(int index) {
    checkElementIndex(index);
    // 获得第 index 的 Node 节点,然后进行移除。
    return unlink(node(index));
}

E unlink(Node x) {
    // assert x != null;
    // 获得 x 的前后节点 prev、next
    final E element = x.item;
    final Node next = x.next;
    final Node prev = x.prev;

    // 将 prev 的 next 指向下一个节点
    if (prev == null) { // 如果 prev 为空,说明 first 被移除,则直接将 first 指向 next
        first = next;
    } else { // 如果 prev 非空
        prev.next = next; // prev 的 next 指向 next
        x.prev = null; // x 的 pre 指向 null
    }

    // 将 next 的 prev 指向上一个节点
    if (next == null) { // 如果 next 为空,说明 last 被移除,则直接将 last 指向 prev
        last = prev;
    } else { // 如果 next 非空
        next.prev = prev; // next 的 prev 指向 prev
        x.next = null; // x 的 next 指向 null
    }

    // 将 x 的 item 设置为 null ,帮助 GC
    x.item = null;
    // 减少链表大小
    size--;
    // 增加数组的修改次数
    modCount++;
    return element;
}

remove(Object o) 方法,移除首个为 o 的元素,并返回是否移除到

public boolean remove(Object o) {
    if (o == null) { // o 为 null 的情况
        // 顺序遍历,找到 null 的元素后,进行移除
        for (Node x = first; x != null; x = x.next) {
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
        // 顺序遍历,找到等于 o 的元素后,进行移除
        for (Node x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;
}

remove() 方法,移除链表首个节点

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

public E removeFirst() {
    final Node f = first;
    // <1> 如果链表为空,抛出 NoSuchElementException 异常
    if (f == null)
        throw new NoSuchElementException();
    // <2> 移除链表时首个元素
    return unlinkFirst(f);
}

private E unlinkFirst(Node f) {
    // assert f == first && f != null;
    final E element = f.item;
    // 获得 f 的下一个节点
    final Node next = f.next;
    // 设置 f 的 item 为 null ,帮助 GC
    f.item = null;
    // 设置 f 的 next 为 null ,帮助 GC
    f.next = null; // help GC
    // 修改 fisrt 指向 next
    first = next;
    // 修改 next 节点的 prev 指向 null
    if (next == null) // 如果链表只有一个元素,说明被移除后,队列就是空的,则 last 设置为 null
        last = null;
    else
        next.prev = null;
    // 链表大小减一
    size--;
    // 增加数组修改次数
    modCount++;
    return element;
}

removeLast() 方法,移除链表最后一个节点

public E removeLast() {
    final Node l = last;
    // 如果链表为空,则抛出 NoSuchElementException 移除
    if (l == null)
        throw new NoSuchElementException();
    // 移除链表的最后一个元素
    return unlinkLast(l);
}

private E unlinkLast(Node l) {
    // assert l == last && l != null;
    final E element = l.item;
    // 获得 f 的上一个节点
    final Node prev = l.prev;
    // 设置 l 的 item 为 null ,帮助 GC
    l.item = null;
    // 设置 l 的 prev 为 null ,帮助 GC
    l.prev = null; // help GC
    // 修改 last 指向 prev
    last = prev;
    // 修改 prev 节点的 next 指向 null
    if (prev == null) // 如果链表只有一个元素,说明被移除后,队列就是空的,则 first 设置为 null
        first = null;
    else
        prev.next = null;
    // 链表大小减一
    size--;
    // 增加数组修改次数
    modCount++;
    return element;
}

7.移除多个元素

removeAll(Collection c) 方法,批量移除指定的多个元素

public boolean removeAll(Collection c) {
    Objects.requireNonNull(c);
    boolean modified = false;
    // 获得迭代器
    Iterator it = iterator();
    // 通过迭代器遍历
    while (it.hasNext()) {
        // 如果 c 中存在该元素,则进行移除
        if (c.contains(it.next())) {
            it.remove();
            modified = true; // 标记修改
        }
    }
    return modified;
}

8.查找单个元素

indexOf(Object o) 方法,查找首个为指定元素的位置

public int indexOf(Object o) {
    int index = 0;
    if (o == null) { // 如果 o 为 null 的情况
        // 顺序遍历,如果 item 为 null 的节点,进行返回
        for (Node x = first; x != null; x = x.next) {
            if (x.item == null)
                return index; // 找到
            index++;
        }
    } else { // 如果 o 非 null 的情况
        // 顺序遍历,如果 item 为 o 的节点,进行返回
        for (Node x = first; x != null; x = x.next) {
            if (o.equals(x.item))
                return index; // 找到
            index++;
        }
    }
    // 未找到
    return -1;
}

9.clear() 方法,清空链表

public void clear() {
  
    // 顺序遍历链表,设置每个节点前后指向为 null
    // 通过这样的方式,帮助 GC
    for (Node x = first; x != null; ) {
        // 获得下一个节点
        Node next = x.next;
        // 设置 x 的 item、next、prev 为空。
        x.item = null;
        x.next = null;
        x.prev = null;
        // 设置 x 为下一个节点
        x = next;
    }
    // 清空 first 和 last 指向
    first = last = null;
    // 设置链表大小为 0
    size = 0;
    // 增加数组修改次数
    modCount++;
}

总结:LinkedList底层是双向链表实现的,在添加或删除数据的时候,LinkedList只需改变节点之间的引用关系,不需要移动数据,所以删除和新增快

你可能感兴趣的:(java基础,java基础)