LinkedList学习

LinkedList

分析

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

存储数据

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

构造函数

// 构造空的集合
public LinkedList() {}

// 构造一个包含指定 collection 的元素的列表
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

常用方法

1、新增

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

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

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

public void add(int index, E element) {
    checkPositionIndex(index);
    if (index == size)
        linkLast(element);
    else
        linkBefore(element, node(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;
    // 数据长度为空,返回 false
    if (numNew == 0)
        return false;
    Node<E> pred, succ;
    if (index == size) {
        // pred=last、succ需要创建新节点绑定到pred上
        succ = null;
        pred = last;
    } else {
        // succ为index处的节点,pred为前驱节点,新节点数据绑定到pred后,
        succ = node(index);
        pred = succ.prev;
    }
    // 遍历数据,添加到节点中
    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;
}

// 添加到尾节点
void linkLast(E e) {
    // 尾节点赋值给  l
    final Node<E> l = last;
    // 创建一个新的尾节点
    final Node<E> newNode = new Node<>(l, e, null);
    // 赋值为 last
    last = newNode;
    if (l == null)
        // 是第一次添加元素,头尾指向本身
        first = newNode;
    else
        // 设置 l 的后继节点
        l.next = newNode;
    size++;
    modCount++;
}

// 添加到某个节点前面
void linkBefore(E e, Node<E> succ) {
    // 获取 succ 的前一个节点
    final Node<E> pred = succ.prev;
    // 创建新节点,绑定下一个节点为 succ
    final Node<E> newNode = new Node<>(pred, e, succ);
    // 设置 succ 的前驱节点
    succ.prev = newNode;
    if (pred == null)
        first = newNode;
    else
        pred.next = newNode;
    size++;
    modCount++;
}

// 添加到头节点
private void linkFirst(E e) {
    final Node<E> f = first;
    // 新建一个头节点,下一节点指向 f
    final Node<E> newNode = new Node<>(null, e, f);
    // 设置新的头节点
    first = newNode;
    if (f == null)
        // 头尾节点都指向自己
        last = newNode;
    else
        // 设置f的前一个节点
        f.prev = newNode;
    size++;
    modCount++;
}

2、删除

// 移除头节点
public E remove() {
    return removeFirst();
}

// 移除头节点
public E removeFirst() {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return unlinkFirst(f);
}

// 移除尾节点
public E removeLast() {
    final Node<E> l = last;
    if (l == null)
        throw new NoSuchElementException();
    return unlinkLast(l);
}

// 移除指定元素
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;
}

// 清空数据
public void clear() {
    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;
    modCount++;
}


private E unlinkFirst(Node<E> f) {
    // 获取节点的值
    final E element = f.item;
    // 重新设置头节点为 f 的后继节点,f 置空
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // help GC
    first = next;
    if (next == null)
        last = null;
    else
        next.prev = null;
    size--;
    modCount++;
    return element;
}

private E unlinkLast(Node<E> l) {
    // 获取节点的值
    final E element = l.item;
    // 重新设置尾节点为 l 的前驱节点,l 置空
    final Node<E> prev = l.prev;
    l.item = null;
    l.prev = null; // help GC
    last = prev;
    if (prev == null)
        first = null;
    else
        prev.next = null;
    size--;
    modCount++;
    return element;
}

3、查找

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

// 获取指定节点
public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}
Node<E> node(int index) {
    // 判断 node 在左边还是右边, 位运算[size/2]
    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;
    }
}

4、修改

public E set(int index, E element) {
    checkElementIndex(index);
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}

5、其它

// 判断是否包含
public boolean contains(Object o) {
    return indexOf(o) != -1;
}

// 判断元素位置
public int indexOf(Object o) {
    int index = 0;
    if (o == null) {
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null)
                return index;
            index++;
        }
    } else {
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item))
                return index;
            index++;
        }
    }
    return -1;
}

// deque 中其它方法

// 获取节点,不删除
public E peek() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}

// 获取节点,删除
public E poll() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}

// 添加到尾节点
public boolean offer(E e) {
    return add(e);
}

// 移除头节点
public E pop() {
    return removeFirst();
}

// 添加到头节点
public void push(E e) {
    addFirst(e);
}

小总结

​ LinkedList与ArrayList一样实现List接口,只是ArrayList是List接口的大小可变数组的实现,LinkedList是List接口链表的实现。

  1. 链表中任意一个存储单元可以向前或向后获取到前一个或后一个存储单元
  2. 链表的尾节点的下一个节点是链表的头节点,链表头节点的前一个节点是链表的尾节点
  • LinkedList在插入和删除时比ArrayList快

  • 随机访问则比ArrayList慢

  • 也是线程不安全的

LinkedList 还实现了 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作

LinkedList是否允许空 允许
LinkedList是否允许重复数据 允许
LinkedList是否有序 有序
LinkedList是否线程安全 非线程安全

切勿使用普通for循环便利LinkedList

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class TestMain {
    private static int SIZE = 111111;

    private static void loopList(List<Integer> list) {
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < list.size(); i++) {
            list.get(i);
        }
        System.out.println(list.getClass().getSimpleName() + "使用普通for循环遍历时间为" +
                (System.currentTimeMillis() - startTime) + "ms");

        startTime = System.currentTimeMillis();
        for (Integer i : list) {}
        System.out.println(list.getClass().getSimpleName() + "使用foreach循环遍历时间为" +
                (System.currentTimeMillis() - startTime) + "ms");
    }

    public static void main(String[] args) {
        List<Integer> arrayList = new ArrayList<Integer>(SIZE);
        List<Integer> linkedList = new LinkedList<Integer>();

        for (int i = 0; i < SIZE; i++) {
            arrayList.add(i);
            linkedList.add(i);
        }
        loopList(arrayList);
        loopList(linkedList);
        System.out.println();
    }
}
------output------
ArrayList使用普通for循环遍历时间为8ms
ArrayList使用foreach循环遍历时间为14ms
LinkedList使用普通for循环遍历时间为8179ms
LinkedList使用foreach循环遍历时间为6ms

​ ArrayList使用for循环遍历快,因为是通过数组索引直接遍历,每次get的时间复杂度为O(1)
​ 使用普通for循环会每次从前一个节点拿后一个节点地址,相当于从头遍历一遍,每次get的时间复杂度为O(N)。
​ 遍历LinkedList时,使用removeFist()或removeLast()效率最高。但用它们遍历时,会删除原始数据

posted @ 2019-07-06 22:51  wansw  阅读(370)  评论(0编辑  收藏  举报