jdk1.7--ArrayList

public class ArrayList<Eextends AbstractList<Eimplements CloneableSerializableRandomAccess


/**
     * The minimum amount by which the capacity of an ArrayList will increase.
     * This tuning parameter controls a time-space tradeoff. This value (12)
     * gives empirically good results and is arguably consistent with the
     * RI's specified default initial capacity of 10: instead of 10, we start
     * with 0 (sans allocation) and jump to 12.<span style="white-space:pre">		</span>//这里最小不再是10
     */
    private static final int MIN_CAPACITY_INCREMENT = 12;

    /**
     * The number of elements in this list.
     */
    int size;

    /**
     * The elements in this list, followed by nulls.
     */
    transient Object[] array;

 成员变量也就是size和一个array数组。

上面这个对象数组就是其存储元素的数据结构,前面有一个java关键字transient,这个关键字是去序列化的意思,即,在这个类序列化后保存到磁盘或者输出到输出流的时候,这个对象数组是不被保存或者输出的。

这里又有疑问了,这个数组不是存储我们保存的数据的吗?为什么要去序列化呢?那么如果去掉序列化之后,我们保存的元素从哪里来呢?

这就跟这个ArrayList的特性有关,我们知道ArrayList的容量,也就是这个数组的容量,一般都是预留一些容量,等到容量不够时再拓展,那么就会出现容量还有冗余的情况,如果这时候进行序列化,整个数组都会被序列化,连后面没有意义空元素的也被序列化。这些是不应该被存储的。所以java的设计者,就为这个类提供了一个writeObject方法,在实现了Serializable接口的类,如果这个类提供了writeObject方法,那么在进行序列化的时候就会通过writeObject方法进行序列化,所以ArrayList的writeObject方法就会显式的为每个实际的数组元素进行序列化,只序列化有用的元素。

private void writeObject(ObjectOutputStream stream) throws IOException {
        stream.defaultWriteObject(); //这里jdk1.8不太一样 1.8中首先有 <span class="keyword" style="font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; line-height: 1.5em; white-space: pre-wrap; text-indent: 1em; font-weight: bold;">int</span><span style="font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; line-height: 1.5em; white-space: pre-wrap; text-indent: 1em; background-color: rgb(246, 246, 246);"> expectedModCount = modCount;</span>
        stream.writeInt(array.length);
        for (int i = 0; i < size; i++) {
            stream.writeObject(array[i]);
        }                           //1.8最后还有一个判断expectedModCount==modCount 不等于抛出异常
    }

好了,回归ArrayList的构造函数

public ArrayList(int capacity) {
        if (capacity < 0) {
            throw new IllegalArgumentException("capacity < 0: " + capacity);
        }
        array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);
    }

    /**
     * Constructs a new {@code ArrayList} instance with zero initial capacity.
     */
    public ArrayList() {
        array = EmptyArray.OBJECT;
    }

这里空参时,并没有初始化容器的大小,而是在add()方法时

public boolean add(E object) {
        Object[] a = array;
        int s = size;
        if (s == a.length) {
            Object[] newArray = new Object[s +
                    (s < (MIN_CAPACITY_INCREMENT / 2) ?
                     MIN_CAPACITY_INCREMENT : s >> 1)]; //如果s小于6,新长度则为s+12,否则为s*3
            System.arraycopy(a, 0, newArray, 0, s);
            array = a = newArray;
        }
        a[s] = object;
        size = s + 1;
        modCount++;
        return true;
    }

下面这个是扩容的方法,在addAll()和add(int index,E e)时被调用,动态设置新的容量大小

private static int newCapacity(int currentCapacity) {
        int increment = (currentCapacity < (MIN_CAPACITY_INCREMENT / 2) ?
                MIN_CAPACITY_INCREMENT : currentCapacity >> 1);
        return currentCapacity + increment;
    }
仍然是保证扩容两倍倍,即大概是currentCapacity*3;下面这个方法是用户自己设置一个合适的最小容量,以便保证添加时的效率问题
/**
     * Ensures that after this operation the {@code ArrayList} can hold the
     * specified number of elements without further growing.
     *
     * @param minimumCapacity
     *            the minimum capacity asked for.
     */
    public void ensureCapacity(int minimumCapacity) {
        Object[] a = array;
        if (a.length < minimumCapacity) {
            Object[] newArray = new Object[minimumCapacity];
            System.arraycopy(a, 0, newArray, 0, size);
            array = newArray;
            modCount++;
        }
    }
再看这个remove方法

public boolean remove(Object object) {
        Object[] a = array;
        int s = size;
        if (object != null) {
            for (int i = 0; i < s; i++) {
                if (object.equals(a[i])) {
                    System.arraycopy(a, i + 1, a, i, --s - i);
                    a[s] = null;  // Prevent memory leak
                    size = s;
                    modCount++;
                    return true;
                }
            }
        } else {
            for (int i = 0; i < s; i++) {
                if (a[i] == null) {
                    System.arraycopy(a, i + 1, a, i, --s - i);
                    a[s] = null;  // Prevent memory leak
                    size = s;
                    modCount++;
                    return true;
                }
            }
        }
        return false;
    }

    可以看出remove和add都有System.arraycopy的复制数组操作,故对于频繁的插入和删除,性能会较差。

其余的方法比如contains(),get(int index),就是遍历找到对应的元素,代码如下

public E get(int index) {
        if (index >= size) {
            throwIndexOutOfBoundsException(index, size);
        }
        return (E) array[index];
    }
public boolean contains(Object object) {
        Object[] a = array;
        int s = size;
        if (object != null) {
            for (int i = 0; i < s; i++) {
                if (object.equals(a[i])) {
                    return true;
                }
            }
        } else {
            for (int i = 0; i < s; i++) {
                if (a[i] == null) {
                    return true;
                }
            }
        }
        return false;
    }

最后说一下这个modCount

我们先说说 容器的“快速失败”机制,它是Java集合中的一种错误检测机制,当多个线程对集合进行结构上的改变的操作时,有可能会产生fail-fast机制。它是一种迭代器检测bug的机制,比如:我们有线程A和线程B,线程A通过Iterator访问集合中的元素,某个时刻,线程B修改了集合中的结构,那么程序就会抛出 ConcurrentModificationException 。在ArrayList中的迭代器中有:

/** The expected modCount value */
        private int expectedModCount = modCount;

在方法中next()和remove()有这段代码

if (ourList.modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
arraylist中每次对数组结构改变一次就modCount+1,因此在迭代过程中,发现modCount != expectedModCount就会抛出异常,保证迭代过程中,arraylist中的数组没有发生改变。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值