手撕系列之LinkedList源码

125 阅读2分钟

一、链表简介

链表:是一种链式的存储结构(像火车一样) 为了解决增加和删除的效率 不便于查询 (和数组相对应)

单链表: 只能从表头开始逐一访问,查询不便 (常见面试题:反转? 如何查询倒数第k个节点?)

双向链表: 增加了一个前置指针(上一个位置) 可以从任一节点开始访问其他节点

节点三要素: 数据、前置指针、后置指针

定义出了首节点和尾节点 首节点的前置指针为null (头节点,串联所有节点的,本身没有数据) 尾节点的后置指针为null

环形链表: 首尾相连 LinkedList在jdk1.6前是环形链表 (常见面试题:约瑟夫环)

二、LinkedList源码

1 、双向链表的特性

a 节点 Node(内部类) ,前置位置prev 后置位置next b 首节点first 尾节点last c 没有大小限制

2 、源码分析

a) 类声明
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable  

i 继承 AbstractSequentialList 以及 实现List接口, 代表有增删改查方法 ii 实现 Deque 双向队列
iii 实现Cloneable ,可以克隆 iv 实现Serializable, 可以被序列化

b) 构造函数

i 无参构造器 ii 有参构造器

    // 可以将集合转化为链表  批量增加
public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
}   
c) 增加元素
public boolean add(E e) {
        //默认从尾部连接节点
        linkLast(e);
        return true;
}
//节点三要素 
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<E> last;
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;
    // 长度加1    
    size++;
    // 更改次数+1
    modCount++;
}
d) 查询元素
public E get(int index) {
        // 先校验索引的有效性 
        checkElementIndex(index);
        // 根据索引找到节点  返回数据
        return node(index).item;
}   
    
private void checkElementIndex(int index) {
    if (!isElementIndex(index))
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

// 判断索引范围是否在[0,size)之间
private boolean isElementIndex(int index) {
    return index >= 0 && index < size;
}

// 提升查询效率??
// 判断索引在前半段还是在后半段  决定从哪儿开始遍历
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;
    }
}