zoukankan      html  css  js  c++  java
  • Java集合--ArrayList源码

    ArrayList是Java的动态数组集合,主要用于快速读取数组元素,且在读多写少的情况下具有较好的表现;相比于LinedList,更节省空间,因为LinkedList的元素还要多存储前后继节点的指针,相较于ArrayList只存储元素本身有一定的差距,但是ArrayList在使用不当的时候也容易浪费内存,例如大量数据不断写入ArrayList时,会不断分配新的内存,并复制原有数据到新的内存中。

    1  /**
    2      * The array buffer into which the elements of the ArrayList are stored.
    3      * The capacity of the ArrayList is the length of this array buffer. Any
    4      * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
    5      * will be expanded to DEFAULT_CAPACITY when the first element is added.
    6      */
    7     transient Object[] elementData; // non-private to simplify nested class access

    elementData数组是ArrayList最重要的属性之一,用于存储实际的数据;elementData为什么会被定义为transient关键字修饰呢?因为ArrayList多数情况下会出现空余数组空间,如果序列化全部的数组,那么会造成时间和空间的额外消耗;ArrayList提供了自己的writeObject方法和readObject方法,只序列化了实际存储的元素。

     /**
         * The size of the ArrayList (the number of elements it contains).
         *
         * @serial
         */
        private int size;

    size是elementData数组中实际存储的元素的个数,区别于elementData.length,在ArrayList中,elementData数组的长度是ArrayList的实际开辟空间的长度

     /**
         * This helper method split out from add(E) to keep method
         * bytecode size under 35 (the -XX:MaxInlineSize default value),
         * which helps when add(E) is called in a C1-compiled loop.
         */
        private void add(E e, Object[] elementData, int s) {
            if (s == elementData.length)
                elementData = grow(); //grow()方法扩充开辟新空间,并复制数据到新空间
            elementData[s] = e;
            size = s + 1;
        }
    
        /**
         * Appends the specified element to the end of this list.
         *
         * @param e element to be appended to this list
         * @return {@code true} (as specified by {@link Collection#add})
         */
        public boolean add(E e) {
            modCount++;
            add(e, elementData, size);
            return true;
        }

    从add(E e)方法中可以分析出,数组中当前元素的个数等于数组的长度时,会增长当前数组的长度;

    /**
         * 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
         * @throws OutOfMemoryError if minCapacity is less than zero
         */
        private Object[] grow(int minCapacity) {
            return elementData = Arrays.copyOf(elementData,
                                               newCapacity(minCapacity));
        }
    
        private Object[] grow() {
            return grow(size + 1);
        }
    
    /**
         * Returns a capacity at least as large as the given minimum capacity.
         * Returns the current capacity increased by 50% if that suffices.
         * Will not return a capacity greater than MAX_ARRAY_SIZE unless
         * the given minimum capacity is greater than MAX_ARRAY_SIZE.
         *
         * @param minCapacity the desired minimum capacity
         * @throws OutOfMemoryError if minCapacity is less than zero
         */
        private int newCapacity(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            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);
        }

    扩充数组容量时,会成倍增长,如果数据量较大又未开辟足够的空间,且不断写入新数据的时候,ArrayList需要不断开辟新空间又要复制数据,性能较差且可能浪费内存空间;

    在add(),set(),remove()等写方法中,会看到modCount得增加,这个字段有什么作用呢?在AbstractList中能看到modCount的定义

     /**
         * The number of times this list has been <i>structurally modified</i>.
         * Structural modifications are those that change the size of the
         * list, or otherwise perturb it in such a fashion that iterations in
         * progress may yield incorrect results.
         *
         * <p>This field is used by the iterator and list iterator implementation
         * returned by the {@code iterator} and {@code listIterator} methods.
         * If the value of this field changes unexpectedly, the iterator (or list
         * iterator) will throw a {@code ConcurrentModificationException} in
         * response to the {@code next}, {@code remove}, {@code previous},
         * {@code set} or {@code add} operations.  This provides
         * <i>fail-fast</i> behavior, rather than non-deterministic behavior in
         * the face of concurrent modification during iteration.
         *
         * <p><b>Use of this field by subclasses is optional.</b> If a subclass
         * wishes to provide fail-fast iterators (and list iterators), then it
         * merely has to increment this field in its {@code add(int, E)} and
         * {@code remove(int)} methods (and any other methods that it overrides
         * that result in structural modifications to the list).  A single call to
         * {@code add(int, E)} or {@code remove(int)} must add no more than
         * one to this field, or the iterators (and list iterators) will throw
         * bogus {@code ConcurrentModificationExceptions}.  If an implementation
         * does not wish to provide fail-fast iterators, this field may be
         * ignored.
         */
        protected transient int modCount = 0;

    modCount用来记录链表被修改的次数;典型的场景是:当迭代器对链表操作时,其他方法修改了该链表,那么通过对比modCount的值就可以判断链表在使用的时候是否被修改过,如果被修改过,立即抛出ConcurrentModificationException异常。这就是AbstractList的fail-fast机制,而不是出现不确定的行为。

    final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }

    checkForComodification()方法定义在ArrayList的私有内部类中,迭代器常用的next方法和remove方法都会调用该方法,用于快速检测链表是否被其他行为修改

    CopyOnWriteArrayList是一个写时拷贝的动态数组,是线程安全的;

        /** The lock protecting all mutators */
        final transient ReentrantLock lock = new ReentrantLock();
    /**
         * Appends the specified element to the end of this list.
         *
         * @param e element to be appended to this list
         * @return {@code true} (as specified by {@link Collection#add})
         */
        public boolean add(E e) {
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                Object[] elements = getArray();
                int len = elements.length;
                Object[] newElements = Arrays.copyOf(elements, len + 1);
                newElements[len] = e;
                setArray(newElements);
                return true;
            } finally {
                lock.unlock();
            }
        }

    在进行add操作的时候,首先获取重入锁,更新数组的副本后在将副本赋值给array。读操作不会加锁

  • 相关阅读:
    Promise前期准备---区别实例对象与函数对象
    es6之函数参数默认值、字符串方法、for of
    es6之剩余和扩展参数
    es6之解构赋值
    es6之set和map
    前端知识点总结
    jQuery的12种选择器
    前端面试总结
    Closure
    PHP 中 16 个魔术方法详解
  • 原文地址:https://www.cnblogs.com/boboshenqi/p/10404052.html
Copyright © 2011-2022 走看看