zoukankan      html  css  js  c++  java
  • Java源码阅读ArrayList

    1简介

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable

    ArrayList使用一个可变数组实现List接口,实现了List接口的所有可选操作。ArrayList除了是非线程安全的之外,其他的与Vector类似。

    2成员属性

    //序列化版本号    
    private static final long serialVersionUID = 8683452581122892189L;
    
    //默认容量
    private static final int DEFAULT_CAPACITY = 10;
    
    //默认数组,没有元素,空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
    
    //默认容量数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
    //存放元素的数组
    transient Object[] elementData; 
    
    //当前ArrayList里面元素存放数量   
    private int size;

    3构造函数

     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[] (see 6260652)
                if (elementData.getClass() != Object[].class)
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
            } else {
                // replace with empty array.
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }

    上面几个构造函数没什么好讲的,但有一个疑问,正常来说DEFAULTCAPACITY_EMPTY_ELEMENTDATA容量应该为10,可是我们在属性的里声明的时候却是为空的,为什么会这样?不急,接下来的扩容代码给出了很好的解释。与Vector一样,ArrayList可以调用trimToSize将多余的空间释放,也可以扩充容量。

    4可收缩容量

    (1)trimToSize方法

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

    size属性是集合里现有元素的个数,elementData.length是数组的实际长度。我们可以trimToSize将数组标号为[size,elementData.length-1]的空间释放。

    (2)扩容

    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);
            }
        }
    
        private static int calculateCapacity(Object[] elementData, int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            return minCapacity;
        }
    
        private void ensureCapacityInternal(int minCapacity) {
            ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
        }
    
        private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
    
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
    
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
        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;
        }

    我们先解释一下DEFAULTCAPACITY_EMPTY_ELEMENTDATA容量问题:

    当调用add方法添加元素时,首先会执行ensureCapacityInternal(size + 1),进入该方法后只有一条ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)),当elementData是DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,calculateCapacity方法会返回DEFAULT_CAPACITY和size + 1的较大值。size默认从0开始的,所以第一次调用add方法时且当elementData是DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,就会对进行扩容操作,而扩容的容量正好是DEFAULT_CAPACITY(10)。

    然后我们分析手动调用ensureCapacity(minCapacity)进行扩容的过程:

    进入该方法后,第一步是求minExpand,这里会判读实际数组是否等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA,若是的话minExpand赋值为DEFAULT_CAPACITY(10),否则为0。然后确保参数minCapacity大于minExpand,才进行后续的扩容操作,调用ensureExplicitCapacity(minCapacity)。

    ensureExplicitCapacity方法确保minCapacity大于当前容量elementData.length才进行扩容操作,调用 grow(minCapacity)。

    因为扩容原理是复制数组,这是个是个很费计算机资源的操作,应该尽量减少扩容次数,所以每次扩容都会保证最小扩容(旧容量*1.5),grow方法的主要功能就是这个。

    5查找

    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;
        }
    
       //查找最后一个索引
        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;
        }
    
        //获取索引位置元素
        public E get(int index) {
            rangeCheck(index);
    
            return elementData(index);
        }
         //set放着一起看吧
         public E set(int index, E element) {
            rangeCheck(index);
    
            E oldValue = elementData(index);
            elementData[index] = element;
            return oldValue;
         }
    
          private void rangeCheck(int index) {
                if (index >= size)
                    throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
          }

    查找的时候注意判断null,因为null.equals()是会报空指针异常的。indexOf是从前往后遍历数组,lastIndexOf则是从后往前遍历。

    6集合基本操作

    (1)添加元素

    //在末尾添加一个元素
    public boolean add(E e) {
            ensureCapacityInternal(size + 1);  // 检查是否需要扩容
            elementData[size++] = e;
            return true;
        }
    
    //在指定位置添加一个元素
     public void add(int index, E element) {
            rangeCheckForAdd(index);
    
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            System.arraycopy(elementData, index, elementData, index + 1,size - index); // index后的所有元素均后移一位
            elementData[index] = element;
            size++;
        }
    
    //添加一堆元素
     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;
        }
    //从指定位置开始添加一堆元素
     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;
        }
    
    private void rangeCheckForAdd(int index) {
            if (index > size || index < 0)
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
    
    

    (2)删除元素

    //删除指定位置的元素
    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;
        }
    
    //删除匹配的元素
     public boolean remove(Object o) {
            if (o == null) {
                for (int index = 0; index < size; index++)
                    if (elementData[index] == null) {
                        fastRemove(index);
                        return true;
                    }
            } else {
                for (int index = 0; index < size; index++)
                    if (o.equals(elementData[index])) {
                        fastRemove(index);
                        return true;
                    }
            }
            return false;
        }
    
     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
        }
    
    //删除索引为[fromIndex,toIndex]范围内的所以元素
    protected void removeRange(int fromIndex, int toIndex) {
            modCount++;
            int numMoved = size - toIndex;
            System.arraycopy(elementData, toIndex, elementData, fromIndex,
                             numMoved);
    
            // clear to let GC do its work
            int newSize = size - (toIndex-fromIndex);
            for (int i = newSize; i < size; i++) {
                elementData[i] = null;
            }
            size = newSize;
        }
    
    //删除一堆元素
    public boolean removeAll(Collection<?> c) {
            Objects.requireNonNull(c); //检查集合c不能为空,否则报空指针异常
            return batchRemove(c, false);
        }
    
     private boolean batchRemove(Collection<?> c, boolean complement) {
            final Object[] elementData = this.elementData;
            int r = 0, w = 0;
            boolean modified = false;
            try {
                for (; r < size; r++)
                    if (c.contains(elementData[r]) == complement)
                        elementData[w++] = elementData[r];
            } finally {
                // Preserve behavioral compatibility with AbstractCollection,
                // even if c.contains() throws.
                if (r != size) {
                    System.arraycopy(elementData, r,
                                     elementData, w,
                                     size - r);
                    w += size - r;
                }
                if (w != size) {
                    // clear to let GC do its work
                    for (int i = w; i < size; i++)
                        elementData[i] = null;
                    modCount += size - w;
                    size = w;
                    modified = true;
                }
            }
            return modified;
        }
    
    //清空(删除所有元素)
    public void clear() {
            modCount++;
    
            // clear to let GC do its work
            for (int i = 0; i < size; i++)
                elementData[i] = null;
    
            size = 0;
        }

    remove和fastRemove的区别是后者没有范围检查,只有确定不会越界的情况下才会调用fastRemove来提升效率。

    值得注意的是与Vector不同,这里删除一堆元素不是通过迭代器实现的,而是通过batchRemove方法,该批量删除方法会遍历数据数组。当complement参数为false时,把不属于参数集合里的元素替换到原数组里面。为true时,把原集合中属于参数集合里的元素替换到原数组里面。

    7序列化方法

    由于ArrayList实现了Serializable接口,我们知道它是可序列化的,而且该类自己实现了序列化writeObject和反序列化readObject方法,当ObjectOutputStream调用writeObject进行序列化时,会调用该类自己的writeObject。同理,ObjectInputStream也会调用该类自己的readObject方法实现反序列化。

    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();
            }
        }
    
       
        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
                int capacity = calculateCapacity(elementData, size);
                SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, 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();
                }
            }
        }

    writeObject方法第一步是获得当前modCount,这样做的目的是确保在方法执行过程中,ArrayList对象没有其他线程修改。然后是调用ObjectOutputStream defaultWriteObject

    方法将ArrayList对象的非static 和非transient的属性写出到当前流。下一步是将集合中当前数据量size写出,最后遍历数组,将每个位置的对象写出。

    readObject方法的工作稍复杂,在进行反序列化之前需要2个工作,第一个是数据检查,确保读过来的是Object数组。第二个是开辟合理的空间存储取回来的数据。

    8迭代器实现

    ArrayList的祖先Collection继承了Iterable接口,ArrayList是通过内部类实现迭代器的。

     public Iterator<E> iterator() {
            return new Itr();
        }
    
        /**
         * An optimized version of AbstractList.Itr
         */
        private class Itr implements Iterator<E> {
            int cursor;       // index of next element to return
            int lastRet = -1; // index of last element returned; -1 if no such
            int expectedModCount = modCount;
    
            Itr() {}
    
            public boolean hasNext() {
                return cursor != size;
            }
    
            @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];
            }
    
            public void remove() {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    ArrayList.this.remove(lastRet);
                    cursor = lastRet;
                    lastRet = -1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
    
            @Override
            @SuppressWarnings("unchecked")
            public void forEachRemaining(Consumer<? super E> consumer) {
                Objects.requireNonNull(consumer);
                final int size = ArrayList.this.size;
                int i = cursor;
                if (i >= size) {
                    return;
                }
                final Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length) {
                    throw new ConcurrentModificationException();
                }
                while (i != size && modCount == expectedModCount) {
                    consumer.accept((E) elementData[i++]);
                }
                // update once at end of iteration to reduce heap write traffic
                cursor = i;
                lastRet = i - 1;
                checkForComodification();
            }
    
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }

    关于ArrayList迭代的实现请参考我的另一篇文章:Java迭代器

    对于ArrayList不仅有其Iterator实现,还有ListIterator的实现。ListIterator不仅可以向后遍历,而且可以向前遍历。

    private class ListItr extends Itr implements ListIterator<E> {
            ListItr(int index) {
                super();
                cursor = index;
            }
    
            public boolean hasPrevious() {
                return cursor != 0;
            }
    
            public int nextIndex() {
                return cursor;
            }
    
            public int previousIndex() {
                return cursor - 1;
            }
    
            @SuppressWarnings("unchecked")
            public E previous() {
                checkForComodification();
                int i = cursor - 1;
                if (i < 0)
                    throw new NoSuchElementException();
                Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length)
                    throw new ConcurrentModificationException();
                cursor = i;
                return (E) elementData[lastRet = i];
            }
    
            public void set(E e) {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    ArrayList.this.set(lastRet, e);
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
    
            public void add(E e) {
                checkForComodification();
    
                try {
                    int i = cursor;
                    ArrayList.this.add(i, e);
                    cursor = i + 1;
                    lastRet = -1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
        }

    ListIterator向后迭代的过程继承自Iterator,这里没什么好说的。这里主要分析previous()set(E e)add(E e)三个方法。

    (1)previous()方法

    该方法定义前向迭代的过程,首先将当前游标cursor向前移动一位,再把游标移动后指向的位置赋值给lastRet,最后返回lastRet指向的值。

    (2)set(E e)方法

    该方法将lastRet指向位置的值改为指定值(参数)

    (3)add(E e)方法

    该方法在cursor位置插入指定值,然后将游标位置后移1位,并将lastRet置为-1。

    9其他

    (1)排序

    ArryList内部提供了排序方法sort,sort方法就是接受了一个Comparator比较器对象,然后将List集合转换成数组,然后再调用数组工具类Arrays的sort()方法进行排序,最后把排好序的数组遍历并赋值到原来的List集合上。

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

    (2)removeIf方法

    该方法的调用依赖于函数编程,需要传入一个函数作为参数,符合该函数判定为true的元素进行删除操作。

     1  public boolean removeIf(Predicate<? super E> filter) {
     2         Objects.requireNonNull(filter);
     3         // figure out which elements are to be removed
     4         // any exception thrown from the filter predicate at this stage
     5         // will leave the collection unmodified
     6         int removeCount = 0;
     7         final BitSet removeSet = new BitSet(size);
     8         final int expectedModCount = modCount;
     9         final int size = this.size;
    10         for (int i=0; modCount == expectedModCount && i < size; i++) {
    11             @SuppressWarnings("unchecked")
    12             final E element = (E) elementData[i];
    13             if (filter.test(element)) {
    14                 removeSet.set(i);
    15                 removeCount++;
    16             }
    17         }
    18         if (modCount != expectedModCount) {
    19             throw new ConcurrentModificationException();
    20         }
    21 
    22         // shift surviving elements left over the spaces left by removed elements
    23         final boolean anyToRemove = removeCount > 0;
    24         if (anyToRemove) {
    25             final int newSize = size - removeCount;
    26             for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
    27                 i = removeSet.nextClearBit(i);
    28                 elementData[j] = elementData[i];
    29             }
    30             for (int k=newSize; k < size; k++) {
    31                 elementData[k] = null;  // Let gc do its work
    32             }
    33             this.size = newSize;
    34             if (modCount != expectedModCount) {
    35                 throw new ConcurrentModificationException();
    36             }
    37             modCount++;
    38         }
    39 
    40         return anyToRemove;
    41     }

    line13的代码filter.test(element)判断符合条件的元素,这里通过位运算记录需要操作的位置,然后进行数组复制。(关于如何通过位运算记录删除位请详细阅读BitSet类源码)

    (3)forEach方法

    该方法和前面迭代器一节中的forEachRemaining类似,也是Java8函数编程新特性。forEach的作用是为集合中的每个元素执行一些操作(操作函数作为参数传入)

     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();
            }
        }

    (4)subList

    List<E> subList(int fromIndex, int toIndex);
    • 该方法返回的是父list的一个视图,从fromIndex(包含),到toIndex(不包含)。fromIndex=toIndex 表示子list为空
    • 父子list做的非结构性修改(non-structural changes)都会影响到彼此:所谓的“非结构性修改”,是指不涉及到list的大小改变的修改。相反,结构性修改,指改变了list大小的修改。
    • 对于结构性修改,子list的所有操作都会反映到父list上。但父list的修改将会导致返回的子list失效。
    • 如何删除list中的某段数据:list.subList(from, to).clear();

    代码这里我就不贴了。

  • 相关阅读:
    rhel5.4 x64安装apache http server2.2.27,并创建自动启服务
    解决BEA-000438 Unable to load performance pack.
    为aix系统挂载iso镜像-命令
    IBM小型机创建RAID
    linux下使用parted工具划分大于2T的分区
    linux下的SElinux
    日立HDS AMS2100存储的调试
    rhel创建yum源
    rfid安全简介
    安卓模拟器抓包
  • 原文地址:https://www.cnblogs.com/ouym/p/8892546.html
Copyright © 2011-2022 走看看