美文网首页
Java基础知识-ArrayList

Java基础知识-ArrayList

作者: liyang_hawk | 来源:发表于2017-10-03 14:50 被阅读0次

    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<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    

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

    内部类:

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

    方法:

    public ArrayList() // 构造方法
    public ArrayList(int initialCapacity)
    public ArrayList(Collection<? extends E> c)
    public boolean add(E e)
    public void add(int index, E element)
    public boolean addAll(int index, Collection<? extends E> c)
    public boolean addAll(Collection<? extends E> 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<? super E> action)
    public E get(int index)
    private void grow(int minCapacity)
    public int indexOf(Object o)
    public boolean isEmpty()
    public Iterator<E> iterator()
    public int lastIndexOf(Object o)
    public ListIterator<E> listIterator()
    public ListIterator<E> 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<? super E> filter)
    protected void removeRange(int fromIndex, int toIndex)
    public void replaceAll(UnaryOperator<E> operator)
    public boolean retainAll(Collection<?> c)
    public E set(int index, E element)
    public int size()
    public void sort(Comparator<? super E> c)
    public Spliterator<E> spliterator()
    public List<E> subList(int fromIndex, int toIndex)
    public Object[] toArray()
    public <T> 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 <tt>List</tt> 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.
     *
     * <p>The best algorithms for manipulating random access lists (such as
     * <tt>ArrayList</tt>) can produce quadratic behavior when applied to
     * sequential access lists (such as <tt>LinkedList</tt>).  Generic list
     * algorithms are encouraged to check whether the given list is an
     * <tt>instanceof</tt> 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.
     *
     * <p>It is recognized that the distinction between random and sequential
     * access is often fuzzy.  For example, some <tt>List</tt> implementations
     * provide asymptotically linear access times if they get huge, but constant
     * access times in practice.  Such a <tt>List</tt> implementation
     * should generally implement this interface.  As a rule of thumb, a
     * <tt>List</tt> implementation should implement this interface if,
     * for typical instances of the class, this loop:
     * <pre>
     *     for (int i=0, n=list.size(); i < n; i++)
     *         list.get(i);
     * </pre>
     * runs faster than this loop:
     * <pre>
     *     for (Iterator i=list.iterator(); i.hasNext(); )
     *         i.next();
     * </pre>
     *
     * <p>This interface is a member of the
     * <a href="{@docRoot}/../technotes/guides/collections/index.html">
     * Java Collections Framework</a>.
     *
     * @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<arr.length; i++) {
                    it.next();
                    it.set(arr[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 <tt>true</tt> (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 <tt>true</tt> if this list changed as a result of the call
         * @throws NullPointerException if the specified collection is null
         */
        public boolean addAll(Collection<? extends E> 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 <tt>true</tt> 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<? extends E> 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<String> list = new ArrayList<String>();
    list.add("a");
    list.add("b");
    list.add("c");
    
    Iterator<String> 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<? super E> 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.
         *
         * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
         *
         * @return an iterator over the elements in this list in proper sequence
         */
        public Iterator<E> 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 <tt>i</tt> such that
         * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
         * 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).
         *
         * <p>The returned list iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
         *
         * @see #listIterator(int)
         */
        public ListIterator<E> listIterator() {
            return new ListItr(0);
        }
    

    readObject方法

    /**
         * Reconstitute the <tt>ArrayList</tt> 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<size; i++) {
                    a[i] = s.readObject();
                }
            }
        }
    

    replaceAll方法

    @Override
        @SuppressWarnings("unchecked")
        public void replaceAll(UnaryOperator<E> 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<? super E> c) {
            final int expectedModCount = modCount;
            Arrays.sort((E[]) elementData, 0, size, c);
            if (modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
            modCount++;
        }
    

    spliterator方法

    /**
         * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
         * and <em>fail-fast</em> {@link Spliterator} over the elements in this
         * list.
         *
         * <p>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<E> 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).
         *
         * <p>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.
         *
         * <p>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 <tt>ArrayList</tt> instance to a stream (that
         * is, serialize it).
         *
         * @serialData The length of the array backing the <tt>ArrayList</tt>
         *             instance is emitted (int), followed by all of its elements
         *             (each an <tt>Object</tt>) 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<size; i++) {
                s.writeObject(elementData[i]);
            }
    
            if (modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
        }
    

    相关文章

      网友评论

          本文标题:Java基础知识-ArrayList

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