阅读LinkedList源码做的一些记录

LinkedList在JDK1.6的实现是基于双向环形链表,在JDK1.8依然是双向链表,但不再是环形的,本文是基于JDK1.8的源码来解析的

一、构造方法以及部分成员变量

1、长度

transient int size = 0;

2、头结点和尾节点

transient Node<E> first;// 头结点
transient Node<E> last;// 尾节点

3、无参构造方法

空的,没什么逻辑

public LinkedList() {
}

4、依据给定的集合来构建LinkedList,先构建一个空的,然后把所有元素都加进去

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

二、增

1、在尾部插入

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

2、插入一个集合的元素(添加在尾部)

public boolean addAll(Collection<? extends E> c) {
    return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
    checkPositionIndex(index);// 检测index的合法性

    Object[] a = c.toArray();// 把集合转为数组
    int numNew = a.length;
    if (numNew == 0)
        return false;
    //这两个变量用来记录插入位置的元素以及它前面的一个元素,如果是在中间插入的话,原index位置上的元素以及其后的元素还要重新链接到LinkedList尾部
    Node<E> pred, succ;
    if (index == size) {
        succ = null;
        pred = last;
    } else {
        succ = node(index);
        pred = succ.prev;
    }
    // 把元素一个一个的添加到LinkedList中
    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;
        pred = newNode;
    }

    if (succ == null) {
        last = pred;
    } else {
        pred.next = succ;
        succ.prev = pred;
    }

    size += numNew;
    modCount++;
    return true;
}

3、在特定位置添加一个元素

public void add(int index, E element) {
    checkPositionIndex(index);// 校验index的合法性
    if (index == size)
        linkLast(element);// 在尾部添加
    else
        linkBefore(element, node(index));
}
//在succ前,添加一个元素
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++;
    modCount++;
}
// 查找特定位置的元素
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;
    }
}

三、删

1、移除特定的元素

public boolean remove(Object o) {
    // 要移除的目标值分两种一种为null,另一种不为null。如果找到并移除成功,则返回true,否则返回false.移除一个目标元素后就返回了,所以如果后面还有相同的值并不会被再次移除
    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) {
    final E element = x.item;
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;
    // 如果prev为null,则表明x为头元素
    if (prev == null) {
        first = next;
    } else {
        prev.next = next;
        x.prev = null;
    }
    // 如果next为null,则表明x为尾元素
    if (next == null) {
        last = prev;
    } else {
        next.prev = prev;
        x.next = null;
    }

    x.item = null;
    size--;
    modCount++;
    return element;
}

2、移除指定位置的元素

public E remove(int index) {
    checkElementIndex(index);// 校验index的合法性
    return unlink(node(index));// 移除指定元素
}

3、清空LinkedList

public void clear() {
    // 循环把一个节点的item、next、prev都置为null
    for (Node<E> x = first; x != null; ) {
        Node<E> next = x.next;
        x.item = null;
        x.next = null;
        x.prev = null;
        x = next;
    }
    first = last = null;
    size = 0;// size归0
    modCount++;
}

四、改

public E set(int index, E element) {
    checkElementIndex(index);// 校验index的合法性
    Node<E> x = node(index);// 获取指定位置的元素
    E oldVal = x.item;
    x.item = element;// 修改值
    return oldVal;
}

五、查

public E get(int index) {
    checkElementIndex(index);// 校验index的合法性
    return node(index).item;// 获取指定位置的值
}

六、其他一些方法

1、获取长度

在增删的时候都对size有维护,获取长度的时候直接返回size就可以了

public int size() {
    return size;
}

2、是否存在指定元素

public boolean contains(Object o) {
    return indexOf(o) != -1;
}

七、遍历

public void testLinkedList(){
    List<String> list = new LinkedList<>();
    for(int i = 0; i < 10000; i ++){
        list.add(i + "");
    }
    String str = "";
    long t = System.currentTimeMillis();
    // 效率极端的低
    for(int j = 0; j < list.size(); j ++){
        str = list.get(j);
    }
    long t2 = System.currentTimeMillis();
    Iterator<String> iterator = list.iterator();
    while(iterator.hasNext()){
        str = iterator.next();
    }
    long t3 = System.currentTimeMillis();
    for(String s : list){
        str = s;
    }
    long t4 = System.currentTimeMillis();
    System.out.println(t2 - t);
    System.out.println(t3 - t2);
    System.out.println(t4 - t3);
}

下图是一些测试数据,可以感受一下,由于foreach是依赖于iterator实现的,所以两者的差别不大
比较
另:LinkedList实现了Deque接口,有Deque的用法,在此不再记述

你可能感兴趣的:(List,LinkedList,java)