面试题首页投稿(暂停使用,暂停投稿)技术文

LinkedList解析,与ArrayList优劣对比

2016-07-29  本文已影响272人  风风风筝

基于JDK1.8
只列出关键方法,关注初始化、add、get、remove

public LinkedList() { //默认没做任何操作
}
public boolean add(E e) {
    linkLast(e);
    return true;
}

transient Node<E> first;
transient 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;
    size++; //size()方法返回值
    modCount++;
}
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;
    }
}

能看出来是这样的结构:

first {pre=null; next = e1; item="a"}
e1 {pre=first; next=e2; item="b"}
e2 {pre=e1; next=e3; item="c"}
...
last {pre=en; next=null; item="x"}

看看get()实现

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

如果index<size/2,则从头部往后查找,反之则从尾部往前查找。

再看remove()实现

public E remove(int index) {
    checkElementIndex(index);
    return unlink(node(index));
}
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;
    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--;
    modCount++;
    return element;
}

先找到index的Node,找到后做的事情也比较简单,比如原本是

first {pre=null; next = e1; item="a"}
e1 {pre=first; next=e2; item="b"}
e2 {pre=e1; next=e3; item="c"}
...
last {pre=en; next=null; item="x"}

remove(1)后

first {pre=null; next = e2; item="a"}
e2 {pre=first; next=e3; item="c"}
...
last {pre=en; next=null; item="x"}

与ArrayList对比

对于get()和set(),ArrayList优于LinkedList,因为LinkedList要遍历查找(最坏情况遍历次数=size/2),而数组可以直接定位。
对于add(index,element),ArrayList需要操作index后面的所有节点往后挪一个位,LinkedList只需修改前后2个node的指向,如果index不是最后一位,那么LinkedList效率更高,如果index是最后一位,实测还是ArrayList效率更高。

ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
    list.add(i);
} //耗时105ms
LinkedList<Integer> list = new LinkedList<>();
for (int i = 0; i < 1000000; i++) {
    list.add(i);
} //耗时220ms

对于remove(index),ArrayList需要把index后面的值都往前挪一个位,LinkedList则需要遍历查找到该index所在的node。

for (int i = 0; i < 10000; i++) {
    list.remove(i);
}

从第一位开始remove,ArrayList耗时4680ms,LinkedList耗时513ms
从末尾开始remove,ArrayList耗时4722ms,LinkedList耗时305ms

总结

get(index)、set(index, element) ArrayList绝对优势
add(element) ArrayList略胜一筹
add(index, element) index越小,ArrayList效率越低,平均来算LinkedList优势
remove() LinkedList绝对优势

上一篇下一篇

猜你喜欢

热点阅读