其实上次我们已经看了ArrayList对象创建后产生的一些现象,下面我们通过源码在来看看往ArrayList集合里面操作对象都有什么现象:
1、增加一个对象,首先看下源代码:
/**
* 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) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
ensureCapacity(size+1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
2、我们可以看出它其实是有两个增加对象的方法,一个就是直接增加,另外一个就是指定位置直接增加,其实他们两个是有很大的不同
。好,那么我们先看看他直接增加的时候是什么样的一个过程:
a) 我们看见增加的第一部里面有个 //Increments modCount!! 的注释--即扩大容器,那么我们进去看看注释前面里面
的方法:
从这个地方的源代码我们可以看出:其实它是判断增加对象后的 minCapacity 是否比之前容器的size(oldCapacity)大,如果大
的话就开始扩充容器,扩充容器的算法是 (size * 3 )/2 + 1 , 它跟vector是不一样的扩容算法,vector是 size * 2,
接着我们可以看到。然后它又检查了一次,然后通过数组的静态工具类Arrays的copyOf方法来扩充了容器,最后返回增加方法:
增加了一个对象,由上面我们还可以看出,其实如果ArrayList是按照正常的增加,即在后面增加对象,其实并没有涉及内容移动
只有在容器不够的时候扩充容器,并无很大的内容开销,换句话说其实它增加一个对象是开销是基本常量的时间。
b)现在我们来看看在ArrayList指定的位置增加一个对象,好,咱们看看它实现的源代码:
ensureCapacity(size+1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
从源代码上我们可以看出,它其实是先判断要增加的位置是否在容器的容量范围内,如果不在,则抛出了我们很熟悉的
异常 IndexOutBoundsException(); 恩~不错,下次如果你不小心捉到这样的异常可千万别说这是什么原因了咕~~(╯﹏╰)b
。好了,我们接着往下看,又看见我们刚刚说的扩容,这里就不在进去看了。接下来就是要在指定位置增加某个对象。
首先,它会用System.arraycopy() 方法把原来的对象从要指定增加的位置向后拷贝一个位置,比如:
现在又个集合有: 1,2,3,4,5 的5个对象,我们要在2位置上增加一个:a 其实它的实现方法是 把 3,4,5 往后面拷贝,这个时候数组
变成: 1,2,3,3,4,5 。然后我们在往下看它在往指定的位置把刚刚指定的对象放上去,这个时候就是:1,2,a,3,4,5可以看见它就是
我们想要的结果了。要了解到这个主要是要理解System.arraycopy();方法的使用则可以了解。我们也可以看见其实往ArrayList
里面指定的位置增加一个对象,如果我们增加的位置不是最后的位置,其实它也没有涉及内存的移动,花费时间跟正常增加一个对象
也是一样的。
其实在指定的位置增加对象还有个问题:比如你指定的位置刚刚好是现在的容易的size + 1的位置,我们是可以正常增加的
从这句代码我们可以看的出,然而如果你增加的位置超过size + 1是会有异常的,这个大家千万要注意了额。额外的说个问题,
很多人觉得很奇怪,为什么我们System.out.println()对象的时候只是打出个地址,而我们打印集合的时候则会看见里面的对
象,这个我们就要追溯到它们的抽象类了,我们会在AbstractCollection里面看见它重写了toString() 方法:
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = i.next();
sb.append(e == this ? "(this Collection)" : e);
if (! i.hasNext())
return sb.append(']').toString();
sb.append(", ");
}
}