Collection框架之ArrayList

原创 2015年11月18日 21:06:18

1.结构

数据结构一直是学习的一大重点,但在Java中它被封装的过于完美,以致于学习Java这么久都不清楚它到底是怎么用的.而且单纯学习数据结构容易学了又忘,所以特此学习了Java Collections Framewoek.

这个框架是java.util程序包的一部分,其中已经实现了大量的数据结构和算法.首先来看看Collection整个结构情况.

Collection结构图

其中关于List接口的UML结构图如下:
这里写图片描述

其中的ArrayList,Vector,LinkedList为相应的实现类,我们在这里主要研究这3个类,使用的JDK版本是1.7.

2.源码分析

2.1 ArrayList

2.1.1 字段

private static final long serialVersionUID = 8683452581122892189L;

private static final int DEFAULT_CAPACITY = 10;

private static final Object[] EMPTY_ELEMENTDATA = {};

private transient Object[] elementData;

private int size;

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

在这里我把对字段的描述去掉了,这样显得篇幅少一点.

  1. serialVersionUID:实现java.io.Serializable类所需要的序列化的值.
  2. DEFAULT_CAPACITY:默认的集合大小为10
  3. EMPTY_ELEMENTDATA:空的数组
  4. size:数组中元素的数量,不是数组的大小
  5. MAX_ARRAY_SIZE:最大数组的大小
  6. elementData:存储元素的集合

在这里的elementData是一个Object[]对象,说明集合是通过数组的形式来存储的,在elementData前面有个transient限定符.
transient限定符说明在串行化时这个字段不保存到某个流中.这样可以节省空间,比如:elementData的长度为10,而其中保存的元素只有5个,那么序列化的时候会保存10个对象,这样就造成了浪费。
从两个序列化的方法可以看出,ArrayList类只保存元素本身,而不是数组.

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
            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方法,该方法将集合大小以及每个元素保存了下来,当反序列化的时候,执行readObject将数据在组装到集合中.

2.1.2 构造器

public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }
 public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    }
public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        size = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }

这其中有3个构造器,第一个构造器创建了一个指定大小的集合数组,第二个构造器创建了一个空的集合数组,第三个构造器创建一个带Collection集合的集合数组.
其中的Arrays.copyOf方法是将elementData中的元素复制到指定size的Object[]中.

2.1.3 add方法

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

ensureCapacityInternal()的功能是判断是否扩展数组大小.
elementData[size++] = e 将数据保存到Object[]数组中

private void ensureCapacityInternal(int minCapacity) {
        if (elementData == EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

如果elementData为空,就在DEFAULT_CAPACITY和minCapacity中找一个最大的.

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

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

如果size+1的长度大于elementData.length的时候进行扩展.如果小于返回.
这里的modCount字段是继承AbstractList抽象类中的,主要用于Iterator迭代器中,到迭代器时会说明用途.

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

在这个方法中,首先获取原始数组集合的大小,其中的oldCapacity >> 1 的意思是将oldCapacity 除以2,这意味着默认扩充的大小为原数组的一半.
最后的Arrays.copyOf(elementData, newCapacity);是将原Object[]数据copy到一个长度为newCapacity的新的Object[]中.

另一个add方法为:

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

这其中先判断下表是否超出,判断是否需要扩充.
System.arraycopy(elementData, index, elementData, index + 1,size - index);将需要添加元素位置之后的元素向后移一位,elementData[index] = element;再将指定位置填充.最后size++

2.1.4 remove方法

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

remove方法先获取oldValue(原来指定位置的元素),再计算出需要向前移动距离,最后移动.返回原始的值.
其中的elementData()如下:

E elementData(int index) {
        return (E) elementData[index];
    }

直接从集合中获取值

另一个remove方法为:

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

这里使用迭代删除,故其消耗的时间和数组长度n成正比,其中的fastRemove()方法为:

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
    }

也是跟上述类似的方法.

2.1.5 其他方法

trimToSize:将elementData数组大小变成同size(元素个数)一样大的Object[].
indexOf(index) : 通过迭代循环获取指定下表的元素.
toArray : 用过Arrays.copyOf方法复制一个数组集合返回
contains(object) : 是否包含指定元素

2.1.6 Iterator迭代器

public interface Collection<E> extends Iterable<E> {
public interface Iterable<T> {

    /**
     * Returns an iterator over a set of elements of type T.
     *
     * @return an Iterator.
     */
    Iterator<T> iterator();
}

由于Collection接口继承Iterable接口,所以子类需要实现该方法,在ArrayList和AbstractList中都实现了iterator()方法.以下为ArrayList中的实现.

public Iterator<E> iterator() {
        return new Itr();
    }
 public ListIterator<E> listIterator() {
        return new ListItr(0);
    }
public ListIterator<E> listIterator(int index) {
        if (index < 0 || index > size)
            throw new IndexOutOfBoundsException("Index: "+index);
        return new ListItr(index);
    }

这里需要两个类Itr和ListItr,这两个类用于进行elementData数组的迭代操作.

 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类中cursor代表指针,指向下一个元素的引用,lastRet代表上一次操作返回的元素,expectedModCount稍后再方法中描述.

public boolean hasNext() {
            return cursor != size;
        }

只要指向下一个元素的位置不为总集合元素长度返回true.

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

这里先检查一下下表是否找不到元素或者越界,cursor = i + 1 游标向后移.
elementData[lastRet = i];我对这种写法经过测试,和elementData[i]没有任何区别,不清楚为什么这么写.

在这其中的checkForComodification()方法 如下:

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

在前面Itr的字段中有expectedModCount = modCount.
在之前的ArrayList方法中,比如add,remove等有对集合进行修改的地方,第一个操作就是modCount++,而且这个modCount是共有的.
这说明在使用迭代器迭代的时候不允许操作数组集合,如果进行操作抛出ConcurrentModificationException()异常.

ListItr类继承Itr类,提供了一些其他的方法,比如:hasPrevious(),previous(),add(),等,实现方式和Itr类似.

2.1.6 subList()

SubList类中有个私有的AbstractList对象,现在不清楚这个方法是干什么用的.

2.2 Vector

Vector类和ArrayList类唯一的重要区别是Vector类的大多数方法是同步的.比如:

public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Java Collection框架 - ArrayList

Java Collection框架 - ArrayList

java 容器类使用 Collection,Map,HashMap,hashTable,TreeMap,List,Vector,ArrayList的区别

java 容器类使用 Collection,Map,HashMap,hashTable,TreeMap,List,Vector,ArrayList的区别 经常会看到程序中使用了记录集,常用的...

Java Collection笔记之ArrayList

1、前言ArrayList 是一个数组队列,相当于 动态数组。与Java中的数组相比,它的容量能动态增长。它继承于AbstractList,实现了List, RandomAccess, Cloneab...
  • p_3er
  • p_3er
  • 2016-03-15 16:34
  • 1197

Java容器类Collection、List、ArrayList、Vector及map、HashTable、HashMap区别

Java容器类Collection、List、ArrayList、Vector及map、HashTable、HashMap区别 Collection是List和Set两个接口的基接口  ...

Java中的ArrayList 、List、LinkedList、Collection关系详解

一、基础介绍(Set、List、Map) Set(集):集合中的元素不按特定方式排序,并且没有重复对象。他的有些实现类能对集合中的对象按特定方式排序。 List(列表):集合中的元素按索引...

JavaSE集合Collection知识点2-----ArrayList------功能

Collection下的ArrayList

迭代器模式和java集合Collection(一)ArrayList

这里主要通过java集合Collection来学习迭代器模式。 一、迭代器模式介绍 1. 一般性UML图   Iterator是迭代器接口,定义了迭代器必须要实现的两个接口hasNext()和next...

Java容器类Collection、List、ArrayList、Vector及map、HashTable、HashMap区

Collection是List和Set两个接口的基接口 List在Collection之上增加了"有序" Set在Collection之上增加了"唯一" 而ArrayList是实现List的...

(黑马程序员)学习笔记,Collection集合(ArrayList、LinkedList、Vector、HashSet、TreeSet)

这类集合都实现了Collection接口,因此也j

05Java语法回顾_collection之arraylist

Java语法Collection之ArrayList读了那么多年的书让我明白一个道理。人要稳重,不要想到啥就做啥。做一行越久即使你不会,几年之后慢慢的你也会了,加上一点努力你或许你能成为别人眼中的专家...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)