1. 他们的底层结构不同

ArrayList 底层是基于数组实现的,ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。

  • LinkedList 底层是基于链表实现的,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。
  • 正因为底层数据结构的不同,他们适用的场景不同,ArrayList 更适合随机查找LinkedList 更适合删除和添加,查询、添加、删除的时间复杂度不同。

2. ArrayList 和 LinkedList 都实现了 List 接口

但是LinkedList还额外实现了Deque接口,正因为 LinkedList 实现了 Deque 接口,所以LinkedList 还可以当作队列来使用。

ArrayList 继承 AbstractList 类,实现 List 等接口

在这里插入图片描述

LinkedList 继承 AbstractSequentialList 类,实现 List 和 Deque 等接口

在这里插入图片描述

3. 查询的对比

  • 指定下标进行查询,ArrayList 优于 LinkedList 。

是由于底层数据结构的原因,数组是提前分配好内存空间的。
对于链表来说,指定下标来查询,是需要遍历链表。

3.1 ArrayList 类中的查询

ArrayList 类中的 get() 方法

    /**
     * Returns the element at the specified position in this list.
     *
     * @param  index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

3.2 LinkedList 类中的查询

LinkedList 类中的 get() 方法 和 node() 方法

    /**
     * Returns the element at the specified position in this list.
     *
     * @param index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
/**
     * Returns the (non-null) Node at the specified element index.
     */
    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;
        }
    }

3.2.1 LinkedList 在查询时存在一种特殊情况

LinkedList 在获取第一个元素和最后一个元素时很快

原因在于,LinkedList 内部有两个属性,分别是 first 和 last 。
这两个属性一直持续的记录着,底层链表里面的第一个元素的位置和最后一个元素的位置在哪里。

/**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;

所以 LinkedList 在查询第一个元素和最后一个元素时很快,因为不涉及遍历。

/**
     * Returns the first element in this list.
     *
     * @return the first element in this list
     * @throws NoSuchElementException if this list is empty
     */
    public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }

    /**
     * Returns the last element in this list.
     *
     * @return the last element in this list
     * @throws NoSuchElementException if this list is empty
     */
    public E getLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }

4. 添加的对比

4.1 ArrayList 的添加操作

ArrayList 的添加操作是存在扩容的情况,并且对于 ArrayList 的添加操作要分情况考虑。

4.1.1 在最后的位置添加元素

不需要移动,需要扩容。

   /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

4.1.2 在指定位置添加元素

指定位置的元素及后面的元素,均往后移动;效率相对较慢。(因为指定了位置,所以不用去遍历查找,但是需要移动元素)

  /**
     * Inserts the specified element at the specified position in this
     * list. Shifts the element currently at that position (if any) and
     * any subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

4.2 LinkedList 的添加操作

LinkedList 的添加操作不涉及扩容

4.2.1 在最后的位置添加元素

   /**
     * Appends the specified element to the end of this list.
     *
     * <p>This method is equivalent to {@link #addLast}.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        linkLast(e);
        return true;
    }

   /**
     * Links e as last element.
     */
    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++;
    }

4.2.2 在指定位置添加元素

需要遍历找到下标,但是插入很快。

    /**
     * Inserts the specified element at the specified position in this list.
     * Shifts the element currently at that position (if any) and any
     * subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }

    /**
     * Inserts element e before non-null Node succ.
     */
    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        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++;
    }

5. 总结

5.1 以下情况使用 ArrayList

  • 频繁访问列表中的某一个元素。
  • 只需要在列表末尾进行添加和删除元素操作。

5.2 以下情况使用 LinkedList

  • 你需要通过循环迭代来访问列表中的某些元素。
  • 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。

与 ArrayList 相比,LinkedList 的增加和删除的操作效率更高,而查找和修改的操作效率较低。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐