LinkedList源码分析整理

LinkedList源码分析整理

转自:https://mp.weixin.qq.com/s/FcTVC7rcq1GXxXa5yySu9w

对比ArrayList


优点:
1、不需要扩容和预留空间,空间效率高

2、增删效率高

缺点:
1、随机访问效率低

2、修改和查询效率低

Node

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

属性

/**
 * 集合元素数量
 */
transient int size = 0;

/**
 * 指向第一个节点的指针
 * Invariant: (first == null && last == null) ||
 *            (first.prev == null && first.item != null)
 */
transient Node<E> first;

/**
 * 指向最后一个节点的指针
 * Invariant: (first == null && last == null) ||
 *            (last.next == null && last.item != null)
 */
transient Node<E> last;

构造方法

public LinkedList() {}

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

添加元素

addAll(Collection c)

将集合c添加到链表,默认添加到尾部。若调用方法addAll(int index, Collection<? extends E> c)方法,则添加到index后面。

public boolean addAll(Collection<? extends E> c) {
    return addAll(size, c);
}

public boolean addAll(int index, Collection<? extends E> c) {
    // 检查index是否合规
    checkPositionIndex(index);

    Object[] a = c.toArray();
    int numNew = a.length;
    if (numNew == 0)
        return false;

    // pred:index位的前置节点
    // succ:index位的后继节点
    Node<E> pred, succ;
    // 如果index是链表的尾部,将插入的第一个节点视为最后一个节点,即Node中next赋值为Null,prev赋值为原链表的末尾值。
    if (index == size) {
        succ = null;
        pred = last;
    } else {
        // 不是尾部(放置在链表中间),取出原index节点,并作为后继节点
        succ = node(index);
        // 将index节点的前置节点作为新插入的第一个节点的前驱节点
        pred = succ.prev;
    }

    // 链表批量增加
    for (Object o : a) {
        // 新建节点,prev = pred, item = e, next = null
        @SuppressWarnings("unchecked") 
        E e = (E) o;
        Node<E> newNode = new Node<>(pred, e, null);
        // 如果prev是Null, 则新添的节点是第一个节点;否则就将index的前置节点的next设置为newNode
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        pred = newNode;
    }

    // 如果后继节点是null,说明是在此列表的队尾追加的
    if (succ == null) {
        // 将尾节点设置为新节点的prev节点
        last = pred;
    } else {
        // 否则是在队中插入节点,更新前置和后继节点
        pred.next = succ;
        succ.prev = pred;
    }

    // 修改size
    size += numNew;
    // 修改modCount
    modCount++;
    return true;
}

/**
 * 返回index处的节点
 */
Node<E> node(int index) {
    // assert isElementIndex(index);
    // 结点在前半段则从first开始遍历,在后半段就从last开始遍历
    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;
    }
}

/**
 * 检测index位置是否合法
 */
private void checkElementIndex(int index) {
    if (!isElementIndex(index))
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

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

addFirst(E e)方法


将e元素添加到链表并设置其头节点

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

/**
 * 将结点e设置成链表的第一个元素
 */
private void linkFirst(E e) {
    final Node<E> f = first;
    final Node<E> newNode = new Node<>(null, e, f);
    first = newNode;
    // 判断原first是否为空,则将last也要指向newNode;否则将newNode的next设置为原first(原first的prev设置为newNode)
    if (f == null)
        last = newNode;
    else
        f.prev = newNode;
    size++;
    modCount++;
}

addLast(E e)方法


将e元素添加到链表并设置其为尾节点(last)

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

/**
 * 将e链接成列表的last元素
 */
void linkLast(E e) {
    final Node<E> l = last;
    // 将新节点的prev设置为原last节点,后继节点为Null
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    // 如果最后一个节点为Null,说明列表中没有元素;否则将原last节点的next指向newNode
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

add(E e)方法


在尾部追加元素e

public boolean add(E e) {
    linkLast(e);
    return true;
}

void linkLast(E e) {
    final Node<E> l = last;
    // 将原last节点设置为newNode的prev节点
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

add(int index, E element)方法


在链表的index处添加元素element

public void add(int index, E element) {
    checkPositionIndex(index);

    if (index == size)
        linkLast(element);
    else
        linkBefore(element, node(index));
}

/**
 * 在succ节点前添加元素e
 */
void linkBefore(E e, Node<E> succ) {
    // assert succ != null;
    // 将succ节点的prev赋值给newNode的prev,将newNode的next赋值为succ
    final Node<E> pred = succ.prev;
    final Node<E> newNode = new Node<>(pred, e, succ);
    succ.prev = newNode;
    // 若prev为空,说明succ是首节点,将first设置为newNode;否则将succ节点的prev指向newNode
    if (pred == null)
        first = newNode;
    else
        pred.next = newNode;
    size++;
    modCount++;
}

get(int index)方法


根据索引获取链表中的元素。

public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}

private void checkElementIndex(int index) {
    if (!isElementIndex(index))
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

Node<E> node(int index) {
    // assert isElementIndex(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;
    }
}

getFirst()方法


获得头节点

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

getLast()方法


获取尾节点

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

删除元素


根据Object对象删除元素

public boolean remove(Object o) {
    if (o == null) {
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;
}

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;
    
    // 将x节点的prev节点的next节点替换为x节点的next节点
    if (prev == null) {
        first = next;
    } else {
        prev.next = next;
        // x.prve已无用处,置空引用
        x.prev = null;
    }

    // 将x节点的next节点的prev节点替换为x节点的prev节点
    if (next == null) {
        last = prev;
    } else {
        next.prev = prev;
        // x.next已无用处,置空引用
        x.next = null;
    }
    // x.item已无用处,置空引用
    x.item = null;
    size--;
    modCount++;
    return element;
}

remove(int index)方法


根据链表的索引删除元素。

public E remove(int index) {
    checkElementIndex(index);
    return unlink(node(index));
}

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;
    // 将列表中的first节点取出,并获得其next节点,随后将其next和item节点置空
    final E element = f.item;
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // help GC
    
    // 将原first节点的next节点改为first节点;如果该结点的next节点为null,那么此时列表中已无节点,否则将next节点的prev节点置空
    first = next;
    if (next == null)
        last = null;
    else
        next.prev = null;
    size--;
    modCount++;
    return element;
}

removeLast()方法


删除尾节点。(类同removeFirst()方法)

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;
    // 将last节点的item和prev节点取出,随后置空last节点的prev和item
    final E element = l.item;
    final Node<E> prev = l.prev;
    l.item = null;
    l.prev = null; // help GC
    
    // 将last节点的prev节点作为新的last节点;如果prev是null表示现在列表已经没有节点,否则就将last节点的prev节点的next节点置空
    last = prev;
    if (prev == null)
        first = null;
    else
        prev.next = null;
    size--;
    modCount++;
    return element;
}

set(int index, E element)


修改元素,先找到Index对应的节点,随后对值进行修改

public E set(int index, E element) {
    checkElementIndex(index);
    // 获得index处的节点,将节点的item属性进行替换。
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LinkedListJava中提供的一个双向链表实现类,其内部维护了一个first和last节点,分别表示链表的头和尾。以下是LinkedList源码分析: 1. 声明LinkedList类 ```java public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable { transient int size = 0; transient Node<E> first; transient Node<E> last; } ``` 2. 声明Node类 ```java 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; } } ``` 3. 实现LinkedList的方法 - add(E e)方法:将元素添加到链表末尾 ```java 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; if (l == null) first = newNode; else l.next = newNode; size++; } ``` - add(int index, E element)方法:将元素插入到指定位置 ```java public void add(int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); } void linkBefore(E e, Node<E> succ) { 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++; } ``` - remove(int index)方法:删除指定位置的元素 ```java public E remove(int index) { checkElementIndex(index); return unlink(node(index)); } E unlink(Node<E> x) { 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--; return element; } ``` - get(int index)方法:获取指定位置的元素 ```java public E get(int index) { checkElementIndex(index); return node(index).item; } Node<E> node(int 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; } } ``` 以上就是LinkedList源码分析,通过对其源码分析,我们可以更深入地理解链表的实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值