ArrayList、LinkedList 分析

List 接口

1 ArrayList

ArrayList 实现了 List 接口,是顺序容器,即元素存放的数据与放进去的顺序相同,允许放入 null 元素,底层通过数组实现

ArrayList 没有实现同步(synchronized),如果需要线程并发访问,可以手动同步,也可使用 Vector 替代。

1 底层数据结构

一个 Object 数组 和 元素数量。

transient Object[] elementData; 
private int size;
复制代码

2 构造方法

Object[] elementData;

  • ArrayList(int initialCapacity) 构造一个大小为 initialCapacity 的数组{}

  • ArrayList() 构造一个 空数组{}

  • ArrayList(Collection<? extends E> c) 根据 容器c 构造数组

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}


public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}


public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] 
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}
复制代码

3 自动扩容

在执行 add 方法时,会调用 ensureCapacityInternal 方法,确认 size+1 时容量是否够用,不够时进行扩容。

  • ensureCapacityInternal(int minCapacity)
  1. 调用了 calculateCapacity 方法,是空数组{} 则返回默认容量 10 ,否则 返回最小容量, 即 add 方法中传入的 size+1
  2. 调用了 ensureExplicitCapacity 方法,如果 最小容量 大于 数组长度,则需要调用 gorw 方法扩容;否则 啥都不做,退回到 add 方法。
  • grow(int minCapacity)
  1. 原数组长度 oldCapacity
  2. 新数组 newCapacity 长度为 oldCapacity + (oldCapacity >> 1),相当于 oldCapacity 的 1.5 倍。
  3. 判断 newCapacity 是否小于最小容量,是则设为最小容量。
  4. 判断 newCapacity 是否超过最大容量 MAX_ARRAY_SIZE,否则调用 hugeCapacity 方法。
  5. 根据新容量,复制新数组,实现扩容。
  • hugeCapacity(int minCapacity) 根据 最小容量minCapacity 来 最大化 容量,超过 MAX_ARRAY_SIZE 则返回 Integer.MAX_VALUE,否则 返回 MAX_ARRAY_SIZE

值得注意的是, newCapacity 扩大为 oldCapacity 的 1.5 倍后,有可能溢出变成负数,此时

  1. 使用 newCapacity - minCapacity > 0 的判断,newCapacity - minCapacity 会溢出又变成正数,判断为 false,可以进入下个溢出判断。
  2. 但如果使用 newCapacity < minCapacity 则为 true,直接修改溢出不符合本身的逻辑。

最终结果是,不论是否溢出,都会保证newCapacity为正数。

总结一下:

  • 空数组 扩容为 10
  • 非空数组扩容为原容量的1.5倍
    • 仍不满足最小容量,扩容为最小容量。如容量1扩容1.5倍也为1
    • 扩容后溢出,根据 最小容量的值 ,扩容为 Integer.MAX_VALUE 或 MAX_ARRAY_SIZE;

数组进行扩容后,会将老数组中的元素重新拷贝一份到新的数组中,使用的是新的内存空间,这种操作的代价是很高的,应该尽量避免数组容量的扩张。指定其容量,或者手动扩容。

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}
复制代码

4.手动扩容

同自动扩容的默认扩容策略,扩容为 1.5倍,若仍小于手动设置的扩容容量,则设为手动设置的大小。手动扩容的效率更高,且可以根据情况扩容,不浪费内存空间。

public void ensureCapacity(int minCapacity) {
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
        // any size if not default element table
        ? 0
        // larger than default for default empty table. It's already
        // supposed to be at default size.
        : DEFAULT_CAPACITY;

    if (minCapacity > minExpand) {
        ensureExplicitCapacity(minCapacity);
    }
}
复制代码

5 调整容量为当前实际大小

public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
        elementData = (size == 0)
          ? EMPTY_ELEMENTDATA
          : Arrays.copyOf(elementData, size);
    }
}
复制代码

2 Vector

3 LinkedList

LinkedList 同时实现了 List 接口和 Deque 接口,也就是说它既可以看作一个顺序容器,又可以看作一个队列 ( Queue ),同时又可以看作一个栈(Stack)。而且包含了队列、双向队列的一些方法。

LinkedList没有实现同步(synchronized),如果需要线程并发访问,可以采用Collections.synchronizedList()方法对其进行包装。

1 底层数据结构

是一个双向链表,头结点 first、尾节点 last、元素数量 size。

transient int size = 0;

transient Node<E> first;

transient Node<E> last;
复制代码

Node 是私有内部类

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;
    }
}
复制代码

2 构造方法

public LinkedList() {
}

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

3 定位方法

对于定位 索引index位置上的节点,使用 index < (size >> 1) 进行判断,离 first 和 last 哪边更近,从哪边开始遍历。

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;
    }
}
复制代码

包含 Queue 方法

// Queue operations.

/**
 * Retrieves, but does not remove, the head (first element) of this list.
 *
 * @return the head of this list, or {@code null} if this list is empty
 * @since 1.5
 */
public E peek() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}

/**
 * Retrieves, but does not remove, the head (first element) of this list.
 *
 * @return the head of this list
 * @throws NoSuchElementException if this list is empty
 * @since 1.5
 */
public E element() {
    return getFirst();
}

/**
 * Retrieves and removes the head (first element) of this list.
 *
 * @return the head of this list, or {@code null} if this list is empty
 * @since 1.5
 */
public E poll() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}

/**
 * Retrieves and removes the head (first element) of this list.
 *
 * @return the head of this list
 * @throws NoSuchElementException if this list is empty
 * @since 1.5
 */
public E remove() {
    return removeFirst();
}

/**
 * Adds the specified element as the tail (last element) of this list.
 *
 * @param e the element to add
 * @return {@code true} (as specified by {@link Queue#offer})
 * @since 1.5
 */
public boolean offer(E e) {
    return add(e);
}
复制代码

包含 Deque 方法

// Deque operations
/**
 * Inserts the specified element at the front of this list.
 *
 * @param e the element to insert
 * @return {@code true} (as specified by {@link Deque#offerFirst})
 * @since 1.6
 */
public boolean offerFirst(E e) {
    addFirst(e);
    return true;
}

/**
 * Inserts the specified element at the end of this list.
 *
 * @param e the element to insert
 * @return {@code true} (as specified by {@link Deque#offerLast})
 * @since 1.6
 */
public boolean offerLast(E e) {
    addLast(e);
    return true;
}

/**
 * Retrieves, but does not remove, the first element of this list,
 * or returns {@code null} if this list is empty.
 *
 * @return the first element of this list, or {@code null}
 *         if this list is empty
 * @since 1.6
 */
public E peekFirst() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
 }

/**
 * Retrieves, but does not remove, the last element of this list,
 * or returns {@code null} if this list is empty.
 *
 * @return the last element of this list, or {@code null}
 *         if this list is empty
 * @since 1.6
 */
public E peekLast() {
    final Node<E> l = last;
    return (l == null) ? null : l.item;
}

/**
 * Retrieves and removes the first element of this list,
 * or returns {@code null} if this list is empty.
 *
 * @return the first element of this list, or {@code null} if
 *     this list is empty
 * @since 1.6
 */
public E pollFirst() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}

/**
 * Retrieves and removes the last element of this list,
 * or returns {@code null} if this list is empty.
 *
 * @return the last element of this list, or {@code null} if
 *     this list is empty
 * @since 1.6
 */
public E pollLast() {
    final Node<E> l = last;
    return (l == null) ? null : unlinkLast(l);
}

/**
 * Pushes an element onto the stack represented by this list.  In other
 * words, inserts the element at the front of this list.
 *
 * <p>This method is equivalent to {@link #addFirst}.
 *
 * @param e the element to push
 * @since 1.6
 */
public void push(E e) {
    addFirst(e);
}

/**
 * Pops an element from the stack represented by this list.  In other
 * words, removes and returns the first element of this list.
 *
 * <p>This method is equivalent to {@link #removeFirst()}.
 *
 * @return the element at the front of this list (which is the top
 *         of the stack represented by this list)
 * @throws NoSuchElementException if this list is empty
 * @since 1.6
 */
public E pop() {
    return removeFirst();
}

/**
 * Removes the first occurrence of the specified element in this
 * list (when traversing the list from head to tail).  If the list
 * does not contain the element, it is unchanged.
 *
 * @param o element to be removed from this list, if present
 * @return {@code true} if the list contained the specified element
 * @since 1.6
 */
public boolean removeFirstOccurrence(Object o) {
    return remove(o);
}

/**
 * Removes the last occurrence of the specified element in this
 * list (when traversing the list from head to tail).  If the list
 * does not contain the element, it is unchanged.
 *
 * @param o element to be removed from this list, if present
 * @return {@code true} if the list contained the specified element
 * @since 1.6
 */
public boolean removeLastOccurrence(Object o) {
    if (o == null) {
        for (Node<E> x = last; x != null; x = x.prev) {
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
        for (Node<E> x = last; x != null; x = x.prev) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;
}
复制代码

猜你喜欢

转载自juejin.im/post/7101531401434398751
今日推荐