ArrayList源码分析

[b]一、类结构[/b]

[img]http://dl2.iteye.com/upload/attachment/0110/1037/e19e7258-8df2-3d3b-ad2b-f64f6fc1599c.png[/img]
List,Set,Queue等均继承自Collection接口。
[b]二、属性[/b]
非常简单就2个属性。
private transient Object[] elementData;
private int size;

[b]三、方法[/b]
提供了增删该查方法,代码都比较简单。
大量使用了System.arraycopy(elementData, index, elementData, index + 1,
size - index);
这个native方法。
第1个elementData:原始数组。
index:原始数组的起始位置
第2个elementData:目的数据。
index+1:目标数组的起始位置。
size - index:要拷贝的长度。
[b]四、扩容[/b]
public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
}

ArrayList的方法add时,先执行这个方法判断是否达到了容量的上限,容量的上限是elementData.length,而当前的数据量是size,如果超过了容量上限,则通过
int newCapacity = (oldCapacity * 3)/2 + 1;计算新的容量上限,
然后通过elementData = Arrays.copyOf(elementData, newCapacity);将原有数据拷贝到容量为newCapacity的新数组中。Arrays.copyOf最终执行的是下面的方法。
Arrays.java
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}


所以如果能预知大小的情况下,最好是设置好ArrayList的初始值,这样可以避免ArrayList的频繁扩容造成的性能损耗。
[b]五、大小限制[/b]
ArrayList能存放的最大数量取决于它的int size属性,所以最大值是Integer.MAX_VALUE(2147483647)。
[b]六、序列化设计[/b]
private transient Object[] elementData;
transient关键字的意思是不进行序列化,但是ArrayList又实现了接口Serializable,貌似有点矛盾啊,既然它的内部数据结构不能序列化,那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 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();
}

}


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

答案就在这里,它实现了readObject和writeObject方法,如果不实现他们就走默认的ObjectOutputStream的defaultWriteObject方法,通过实现这两个方法可以自定义序列化和反序列化的规则。
注意其中用的是i<size,而不是i<elementData.length,这样就保证了只序列化真实有效的数据,避免浪费,这也就是将elementData设置为transient的原因吧。
[b]七、modCount[/b]
ArrayList的add和remove等改变结构的方法都执行了代码modCount++,这是干什么用的呢?
int expectedModCount = modCount;

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

public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}

AbstractList中通过next()进行遍历时,会先执行checkForComodification()方法,该方法
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}

如果不相等的话就会抛出ConcurrentModificationException异常,也就是不允许一个线程遍历的时候其他线程修改这个ArrayList。
抛异常示例代码:
public class Tester {
public static void main(String[] args) throws Exception{
List<String> list = new ArrayList<String>();
for(long i=0;i<10;i++){
list.add(i+"");
}
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
list.add("haha");
String it = iterator.next();
System.out.println(it);
}
}


}

[b]八、同Vector区别[/b]
Vector同ArrayList的实现基本上差不多,只是Vector是线程安全的方法前面都加了synchronized,另外Vector允许设置capacityIncrement,默认是0,如果等于0的话,以每次2倍的速度增长。
  public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);

return (E)elementData[index];
}

[b]九、CopyOnWriteArrayList[/b]
CopyOnWriteArrayList是并发包中的ArrayList的一个版本,它读不加锁,写加锁,非常适用于读多写少且允许部分脏数据的场景。
  public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();//加锁
try {
Object[] elements = getArray();
int len = elements.length;
//每次都创建一个新的数组。
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();//解锁
}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值