ArrayList是数组元素常用的数据结构,比较简单。
主要从以下几点总结以下该结构。
ArrayList的基本属性:
//对象数组:ArrayList的底层数据结构,用于存放数据,
private transient Object[] elementData;
//elementData中已存放的元素的个数,注意:不是elementData的容量
private int size;
是不是对transient的使用有点迷糊?下面讲到
ArrayList的内容序列化
transient关键字的作用:在采用Java默认的序列化机制的时候,被该关键字修饰的属性不会被序列化。
ArrayList类实现了java.io.Serializable接口,即采用了Java默认的序列化机制
上面的elementData属性采用了transient来修饰,表明其不使用Java默认的序列化机制来实例化,但是该属性是ArrayList的底层数据结构,在网络传输中一定需要将其序列化,之后使用的时候还需要反序列化,那不采用Java默认的序列化机制,那采用什么呢序列化方式呢?
源码有两个方法,ArrayList自己实现了序列化和反序列化
/**
* Save the state of the <tt>ArrayList</tt> instance to a stream (that is,
* serialize it).
*
* @serialData The length of the array backing the <tt>ArrayList</tt>
* instance is emitted (int), followed by all of its elements
* (each an <tt>Object</tt>) in the proper order.
*/
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 array length
s.writeInt(elementData.length);
// 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();
}
}
/**
* Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in array length and allocate array
int arrayLength = s.readInt();
Object[] a = elementData = new Object[arrayLength];
// Read in all elements in the proper order.
for (int i = 0; i < size; i++)
a[i] = s.readObject();
}
ArrayList的构造器:
/**
* 创建一个对象数组,容量为initialCapacity,
*/
public ArrayList(int initialCapacity) {
super();
AbstractList() {}
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity:" + initialCapacity);
this.elementData = new Object[initialCapacity];
}
/**
* 默认初始化一个容量为10的对象数组
*/
public ArrayList() {
this(10);
}
当执行new ArrayList<String>()时,会调用上边的无参构造器,创造一个容量为10的对象数组。
在我们执行new ArrayList<String>(4)时,会调用上边的public ArrayList(int initialCapacity),创造一个容量为4的对象数组。
在实际使用中,如果是成员变量可以确定的场景,建议直接初始化时定好容量。
ArrayList扩容
/**
* 确保数组的容量足够,若是不够,必须扩容
*/
public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length;//获取数组的容量
//容量满的情况下
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3) / 2 + 1;//新容量为旧容量的1.5倍+1
if (newCapacity < minCapacity)//如果扩容后的新容量还不满足新增要求
newCapacity = minCapacity;//新容量设为预期需要的容量
elementData = Arrays.copyOf(elementData, newCapacity);//复制元素并使用新的容量大小
}
}
在上述代码的扩容结束后,调用了Arrays.copyOf(elementData, newCapacity)方法,这个方法先创建了一个新的容量为newCapacity的对象数组,然后使用System.arraycopy()方法将旧的对象数组复制到新的对象数组中去了。
注意:
modCount变量用于在遍历集合(iterator())时,检测是否发生了add、remove操作。
删除ArrayList中的对象
remove(Object o)需要遍历数组,remove(int index)不需要,只需要判断索引符合范围即可,所以,通常:后者效率更高。
遍历ArrayList中的对象(iterator方式)
源代码:iterator()方法是在内部类AbstractList中实现的,该方法返回AbstractList的一个内部类Itr对象
public Iterator<E> iterator() {
return new Itr();//返回一个内部类对象
}
Itr内部类:
private class Itr implements Iterator<E> {
int cursor = 0;//标记遍历到的元素位置
int expectedModCount = modCount;
//ArrayList修改次数,用于fast fail机制快速失败 原理详见HashMap篇
//检测对象数组是否还有元素
public boolean hasNext() {
return cursor != size();//如果cursor==size,表示遍历结束
}
//获取下一元素
public E next() {
checkForComodification();//检测在遍历的过程中,ArrayList是否发生了内容变化
try {
E next = get(cursor++);
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
//检测在遍历的过程中,是否发生内容变化
final void checkForComodification() {
if (modCount != expectedModCount)//预期修改次数是否等于真的修改次数
throw new ConcurrentModificationException();
}
}
ArrayList基于数组方式实现,容量限制几乎无限
ArrayList线程不安全