ArrayList的个人体会
最近Java学习时,我们开始去看ArrayList的原码,接下来就让我来说一下我在看完原码后自己的一些心得体会。
首先,在ArrayList中定义了几个数据
private static final long serialVersionUID = 8683452581122892189L;
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
private int size;
/**
* elementData 是承载器,用于储存数组元素
* DEFAULT_CAPACITY 是默认的容量大小
* EMPTY_ELEMENTDATA 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 是两个空数组
* size 代表的不是 elementData 的长度,而是ArrayList的元素个数
* 第一行的 serialVersionUID 没有看懂,不知道有什么作用
*/
ArrayList的构造方法
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}
/**
* 第一个有参构造就是创建一个指定容量的数组链表
* 无参构造就是创建一个默认的空数组
* 第二个有参构造按照集合的迭代器返回元素的顺序构造包含指定集合元素的列表,即指定元素的初始化
*/
ArrayList 的大部分方法与 LinkedList 一致,add() 、remove() 、get() 、set() 啥的。我感觉,ArrayList 中,与 LinkedList 不同且我认为最重要的一部分方法就是 ArrayList 的扩容的方法。
ArrayList 的扩容方法总共有5个,原码如下
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
? 0
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
这个扩容的过程有点复杂,于是我自己写了一个比较简单的扩容
public void judgeCapacity(int needCapacity) {
if(needCapacity > elementData.length) {
Object oldElementData[] = elementData;
int newSize = this.size * 2 + 1; // 扩容的空间
elementData = new Object[newSize];
elementData = Arrays.copyOf(oldElementData, newSize); // 将之前的数组拷贝到新的数组中去
}
}
所谓扩容,就是判断需要的容量和给予的容量谁大谁小,如果需要的容量小,就不需要扩容,否则,就需要扩容。
当然,我的这个扩容非常的简单,肯定还是有不够完善的地方的,这也是我在写自己的 ArrayList 时用到的扩容方法,但是就目前来说并没有遇到什么问题。
然后就是其中的 clone( ) 方法,该方法返回此 ArrayList 实例的浅拷贝,元素本身不被复制。原码如下
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
在我的理解中,大致实现的过程就是先创建一个对象实例 v ,然后调用 v 中的 elementData 去拷贝原来的 elementData,最后再返回 v,这样就完成了元素的浅拷贝。
关于什么是浅拷贝、什么是深拷贝我也去做了一些了解。
我对于这两种拷贝方式的理解,浅拷贝就是设置一个新的对象,这个新的对象拷贝了旧的对象的内存地址,即两者是同一个对象,因此其中一个对象改变了这个地址,就会影响到另一个对象。示意图大致如下:
而深拷贝就是直接创建了一个新的对象,改变其中一个对象并不会影响到另一个对象。如图: