Java基础知识-ArrayList

ArrayList:

  java -version

  java version "1.8.0_77"
  Java(TM) SE Runtime Environment (build 1.8.0_77-b03)
  Java HotSpot(TM) 64-Bit Server VM (build 25.77-b03, mixed mode)
public class ArrayList extends AbstractList
        implements List, RandomAccess, Cloneable, java.io.Serializable

  • 实现了List接口的可变大小的数组。
  • 实现所有元素(包括null值)的任意list操作。
  • 除了实现List接口,这个类还提供了方法本地调整数组的大小。(与Vector相比,此类是unsynchronized,线程不安全的)。
  • 方法size,isEmpty,get,set,iterator,listIterator的操作花费时间固定;而相对的add,remove的操作时间是不固定的(因为会影响整个数组的重新复制)。

内部类:

static final class ArrayListSpliterator implements Spliterator
private class Itr implements Iterator
private class ListItr extends Itr implements ListIterator
private class SubList extends AbstractList implements RandomAccess

方法:

public ArrayList() // 构造方法
public ArrayList(int initialCapacity)
public ArrayList(Collection c)
public boolean add(E e)
public void add(int index, E element)
public boolean addAll(int index, Collection c)
public boolean addAll(Collection c)
private boolean batchRemove(Collection c, boolean complement)
public void clear()
public Object clone()
public boolean contains(Object o)
E elementData(int index)
public void ensureCapacity(int minCapacity)
private void ensureCapacityInternal(int minCapacity)
private void ensureExplicitCapacity(int minCapacity)
private void fastRemove(int index)
public void forEach(Consumer action)
public E get(int index)
private void grow(int minCapacity)
public int indexOf(Object o)
public boolean isEmpty()
public Iterator iterator()
public int lastIndexOf(Object o)
public ListIterator listIterator()
public ListIterator listIterator(int index)
private String outOfBoundsMsg(int index)
private void rangeCheck(int index)
private void rangeCheckForAdd(int index)
private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException
public E remove(int index)
public boolean remove(Object o)
public boolean removeAll(Collection c)
public boolean removeIf(Predicate filter)
protected void removeRange(int fromIndex, int toIndex)
public void replaceAll(UnaryOperator operator)
public boolean retainAll(Collection c)
public E set(int index, E element)
public int size()
public void sort(Comparator c)
public Spliterator spliterator()
public List subList(int fromIndex, int toIndex)
public Object[] toArray()
public  T[] toArray(T[] a)
public void trimToSize()
private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException

变量:

/**
 * The size of the ArrayList (the number of elements it contains).
 * ArrayList的大小(即包涵元素个数)
 */
private int size; 

/**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     * 
     * 储存ArrayList元素的对象。
     */
transient Object[] elementData;

常量:

/**
 * The maximum size of array to allocate.
 * Some VMs reserve some header words in an array.
 * Attempts to allocate larger arrays may result in
 * OutOfMemoryError: Requested array size exceeds VM limit
 *
 * 数组能申请到的最大空间,否则报错OutOfMemoryError
 *
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private static final int DEFAULT_CAPACITY = 10;

Question:

1. ArrayList的最大可申请空间是多少?
自我理解,size是标识ArrayList大小的字段,类型为int,故最大空间为Integer.MAX_VALUE,也就是2^31,大概为21亿左右,查看源码:

 /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

会减去8,意思是有些虚拟机会保留headerwords在数组里面,所以需要预留减去一个8。
参考地址:
Why the maximum array size of ArrayList is Integer.MAX_VALUE - 8?
另外,根据下面:

The maximum number of elements in an array in JDK 6 and above is Integer.MAX_VALUE - 2 = 2 147 483 645. Java successfully allocates such an array if you run it with -Xmx13G. It fails with OutOfMemoryError: Java heap space if you pass -Xmx12G

ArrayList的大小到不了最大值,除非给出足够的VM可用内存。

2. RandomAccess接口的含义?

package java.util;

/**
 * Marker interface used by List implementations to indicate that
 * they support fast (generally constant time) random access.  The primary
 * purpose of this interface is to allow generic algorithms to alter their
 * behavior to provide good performance when applied to either random or
 * sequential access lists.
 *
 * 

The best algorithms for manipulating random access lists (such as * ArrayList) can produce quadratic behavior when applied to * sequential access lists (such as LinkedList). Generic list * algorithms are encouraged to check whether the given list is an * instanceof this interface before applying an algorithm that would * provide poor performance if it were applied to a sequential access list, * and to alter their behavior if necessary to guarantee acceptable * performance. * *

It is recognized that the distinction between random and sequential * access is often fuzzy. For example, some List implementations * provide asymptotically linear access times if they get huge, but constant * access times in practice. Such a List implementation * should generally implement this interface. As a rule of thumb, a * List implementation should implement this interface if, * for typical instances of the class, this loop: *

 *     for (int i=0, n=list.size(); i < n; i++)
 *         list.get(i);
 * 
* runs faster than this loop: *
 *     for (Iterator i=list.iterator(); i.hasNext(); )
 *         i.next();
 * 
* *

This interface is a member of the * * Java Collections Framework. * * @since 1.4 */ public interface RandomAccess { }

jdk1.2开始引入的一种标记接口的模式,在迭代遍历的时候使用,需要的话自己逻辑里面添加对该标记的识别。
被标记为RandomAccess的类,通过下标方式访问collection是常数时间的,而对于非此类的(Linkedlist)使用下标方式将花费大量时间索引下标。下面举例Collections.shuffle方法,使用list时先判断类型后排序。

public static void shuffle(List list, Random rnd) {
        int size = list.size();
        if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
            for (int i=size; i>1; i--)
                swap(list, i-1, rnd.nextInt(i));
        } else {
            Object arr[] = list.toArray();

            // Shuffle array
            for (int i=size; i>1; i--)
                swap(arr, i-1, rnd.nextInt(i));

            // Dump array back into list
            // instead of using a raw type here, it's possible to capture
            // the wildcard but it will require a call to a supplementary
            // private method
            ListIterator it = list.listIterator();
            for (int i=0; i

参考知乎某文章:arraylist randomaccess
实际对比RandomAccess和Iterator文章:RandomAccess 接口使用

  1. Cloneable接口的含义是多什么?
  2. Serializable是如何实现的?

方法add操作花费时间不定,
第一种:add方法将元素添加到数组最后面,每次添加前先检测数组的容量,检测如果空间不足,将当前数组申请新的增长空间后复制过去。

/**
     * Appends the specified element to the end of this list.
     * 添加元素到数组list的最后面
     * @param e element to be appended to this list
     * @return true (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

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

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    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);
    }
/**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    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);
    }

第二种是在固定位置添加元素,角标为index的值以后向后移动,最后赋值index位置。

/**
     * 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++;
    }

第三种是添加一个集合到该数组的最后面。

 /**
     * Appends all of the elements in the specified collection to the end of
     * this list, in the order that they are returned by the
     * specified collection's Iterator.  The behavior of this operation is
     * undefined if the specified collection is modified while the operation
     * is in progress.  (This implies that the behavior of this call is
     * undefined if the specified collection is this list, and this
     * list is nonempty.)
     *
     * @param c collection containing elements to be added to this list
     * @return true if this list changed as a result of the call
     * @throws NullPointerException if the specified collection is null
     */
    public boolean addAll(Collection c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

第四种是添加一个集合到数组的指定位置。
System.arraycopy参数解释:原数组,原起始位置,目标数组,目标位置,大小。

 /**
     * Inserts all of the elements in the specified collection into this
     * list, starting at the specified position.  Shifts the element
     * currently at that position (if any) and any subsequent elements to
     * the right (increases their indices).  The new elements will appear
     * in the list in the order that they are returned by the
     * specified collection's iterator.
     *
     * @param index index at which to insert the first element from the
     *              specified collection
     * @param c collection containing elements to be added to this list
     * @return true if this list changed as a result of the call
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @throws NullPointerException if the specified collection is null
     */
    public boolean addAll(int index, Collection c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

ArrayList中modCount和expectedModCount,进行增删add,remove等修改涉及结构变化的方法中都会触发modCount的改变,而内部Iterator的expectedModCount同步了modCount,遍历的时候不允许进行新增和删除操作的简单逻辑。

@SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }
final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

以下情形会发生ConcurrentModificationException的情况。

List list = new ArrayList();
list.add("a");
list.add("b");
list.add("c");

Iterator it = list.iterator();
while(it.hasNext()) {
    String str = it.next();
    if (str.equals("a")) list.remove("a");
}
for (String value: list) {
    if (value.equals("a")) {
        list.remove("a");
    }
    System.out.println(value);
}

Arraylist中的modCount
RandomAccess接口理解

indexOf方法

public boolean contains(Object o) {
    return indexOf(o) >= 0;
}

public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

fastRemove方法

/*
     * Private remove method that skips bounds checking and does not
     * return the value removed.
     */
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }
/**
     * Removes the element at the specified position in this list.
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).
     *
     * @param index the index of the element to be removed
     * @return the element that was removed from the list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }
/**
     * Removes the element at the specified position in this list.
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).
     *
     * @param index the index of the element to be removed
     * @return the element that was removed from the list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

forEach方法

@Override
    public void forEach(Consumer action) {
        Objects.requireNonNull(action);
        final int expectedModCount = modCount;
        @SuppressWarnings("unchecked")
        final E[] elementData = (E[]) this.elementData;
        final int size = this.size;
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            action.accept(elementData[i]);
        }
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

iterator遍历

/**
     * Returns an iterator over the elements in this list in proper sequence.
     *
     * 

The returned iterator is fail-fast. * * @return an iterator over the elements in this list in proper sequence */ public Iterator iterator() { return new Itr(); }

lastIndexOf方法

 /**
     * Returns the index of the last occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * More formally, returns the highest index i such that
     * (o==null ? get(i)==null : o.equals(get(i))),
     * or -1 if there is no such index.
     */
    public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

listIterator方法

/**
     * Returns a list iterator over the elements in this list (in proper
     * sequence).
     *
     * 

The returned list iterator is fail-fast. * * @see #listIterator(int) */ public ListIterator listIterator() { return new ListItr(0); }

readObject方法

/**
     * Reconstitute the ArrayList instance from a stream (that is,
     * deserialize it).
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        elementData = EMPTY_ELEMENTDATA;

        // Read in size, and any hidden stuff
        s.defaultReadObject();

        // Read in capacity
        s.readInt(); // ignored

        if (size > 0) {
            // be like clone(), allocate array based upon size not capacity
            ensureCapacityInternal(size);

            Object[] a = elementData;
            // Read in all elements in the proper order.
            for (int i=0; i

replaceAll方法

@Override
    @SuppressWarnings("unchecked")
    public void replaceAll(UnaryOperator operator) {
        Objects.requireNonNull(operator);
        final int expectedModCount = modCount;
        final int size = this.size;
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            elementData[i] = operator.apply((E) elementData[i]);
        }
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

set方法

/**
     * Replaces the element at the specified position in this list with
     * the specified element.
     *
     * @param index index of the element to replace
     * @param element element to be stored at the specified position
     * @return the element previously at the specified position
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

sort方法

@Override
    @SuppressWarnings("unchecked")
    public void sort(Comparator c) {
        final int expectedModCount = modCount;
        Arrays.sort((E[]) elementData, 0, size, c);
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

spliterator方法

/**
     * Creates a late-binding
     * and fail-fast {@link Spliterator} over the elements in this
     * list.
     *
     * 

The {@code Spliterator} reports {@link Spliterator#SIZED}, * {@link Spliterator#SUBSIZED}, and {@link Spliterator#ORDERED}. * Overriding implementations should document the reporting of additional * characteristic values. * * @return a {@code Spliterator} over the elements in this list * @since 1.8 */ @Override public Spliterator spliterator() { return new ArrayListSpliterator<>(this, 0, -1, 0); }

/**
     * Returns an array containing all of the elements in this list
     * in proper sequence (from first to last element).
     *
     * 

The returned array will be "safe" in that no references to it are * maintained by this list. (In other words, this method must allocate * a new array). The caller is thus free to modify the returned array. * *

This method acts as bridge between array-based and collection-based * APIs. * * @return an array containing all of the elements in this list in * proper sequence */ public Object[] toArray() { return Arrays.copyOf(elementData, size); }

writeObject方法

/**
     * Save the state of the ArrayList instance to a stream (that
     * is, serialize it).
     *
     * @serialData The length of the array backing the ArrayList
     *             instance is emitted (int), followed by all of its elements
     *             (each an Object) in the proper order.
     */
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // Write out element count, and any hidden stuff
        int expectedModCount = modCount;
        s.defaultWriteObject();

        // Write out size as capacity for behavioural compatibility with clone()
        s.writeInt(size);

        // Write out all elements in the proper order.
        for (int i=0; i

你可能感兴趣的:(Java基础知识-ArrayList)