美文网首页
ArrayList和LinkedList的区别

ArrayList和LinkedList的区别

作者: 望穿天堂 | 来源:发表于2019-08-21 11:32 被阅读0次

ArrayList和LinkedList都实现了List接口。

ArrayList是基于索引的数据结构,它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问, 但是要随机插入或删除数据却是开销很大的,因为这需要重排数组中的所有数据。在ArrayList的当前容量足够大时,向最后插入数据开销很小(时间复杂度O(1)),在ArrayList的当前容量不足时,需要进行扩容。

LinkedList基于双向链表,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)。相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引,只需要改变指针指向即可。LinkedList由于需要存储每个节点的prev和next,所以需要的内存更多。

ArrayList

get(index)读取下标,时间复杂度O(1)

transient Object[] elementData;

public E get(int index) {

    Objects.checkIndex(index, size);

    return elementData(index);

}

E elementData(int index) {

    return (E)elementData[index];

}

add(E)向队尾插入数据

public boolean add(E e) {
    modCount++;
    add(e, elementData, size);
    return true;
}

private void add(E e, Object[] elementData, int s) {
    // 当容量不足时,进行扩容
    if (s == elementData.length)
        elementData = grow();
    elementData[s] = e;
    size = s + 1;
}

private Object[] grow() {
    return grow(size + 1);
}

private Object[] grow(int minCapacity) {
    return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
}

private int newCapacity(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    // newCapacity为计算出的新容量
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity <= 0) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
            return minCapacity;
        }
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }

add(index, E)向指定位置插入数据
调用System.arraycopy

public void add(int index, E element) {
    rangeCheckForAdd(index);
    modCount++;
    final int s;
    Object[] elementData;
    if ((s = size) == (elementData = this.elementData).length)
        elementData = grow();
    System.arraycopy(elementData, index,
                     elementData, index + 1,
                     s - index);
    elementData[index] = element;
    size = s + 1;
}

remove:无论是remove(index)还是remove(E),调用的都是fastRemove(Object[] es, int i),remove(index)直接调用fastRemove(时间复杂度O(1)),而remove(E)需要先遍历数组得到E的index(时间复杂度O(n))
最终调用System.arraycopy

private void fastRemove(Object[] es, int i) {
    modCount++;
    final int newSize;
    if ((newSize = size - 1) > i)
        System.arraycopy(es, i + 1, es, i, newSize - i);
    es[size = newSize] = null;
}

LinkedList

get(index)读取下标,时间复杂度O(n)

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

add(E)向后插入数据

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

void linkLast(E e) {
    final Node<E> l = last;
    // 新节点的上一个节点为之前的last节点
    final Node<E> newNode = new Node<>(l, e, null);
    // 当前的last节点为新节点
    last = newNode;
    // 在本次插入之前LinkedList是一个空list,那么first和last均为新节点,且这个节点的next和prev均为null
    if (l == null)
        first = newNode;
    else
        // 之前的last节点的下一个节点为新节点
        l.next = newNode;
    size++;
    modCount++;
}

add(index, E)向指定位置插入数据
a <-> b <-> c
假设node(index)拿到的是b
那么new的prev为a,next为b
修改b的prev为new
修改a的next为new
a <-> new <->b <-> c

public void add(int index, E element) {
    checkPositionIndex(index);
    // 当index==size时,相当于向尾部增加数据
    if (index == size)
        linkLast(element);
    else
        // node(index)同样在get(index)时被调用,时间复杂度O(n)
        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++;
    modCount++;
}

remove(index)

public E remove(int index) {
    checkElementIndex(index);
    // node(index)同样在get(index)时被调用,时间复杂度O(n)
    return unlink(node(index));
}

remove(E)
遍历链表找到E,时间复杂度O(n)

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

unlink
a <-> b <-> c
假设需要删除的是b
修改a的next为c
修改c的prev为a
a <-> c

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

相关文章

网友评论

      本文标题:ArrayList和LinkedList的区别

      本文链接:https://www.haomeiwen.com/subject/rddbsctx.html