您的位置:首页 > 其它

ArrayList源码简略解读

2017-08-08 10:45 399 查看
     众所周知,ArrayList的底层是数组,作为经常使用的集合,作为一位java开发 对于此集合想必不陌生。相对于HashMap的源码,ArrayList源码相对简单。言归正传,让我们简略的走进ArrayList的源码之中。

   我们从new 一个ArrayList开始,

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;

public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}


先简单说明下: transient 关键字标记的成员变量不参与序列化过程。当我们new一个ArrayList的时候,elementData指向了一个Object的数组。

当我们调用add方法的时候,

private static final int DEFAULT_CAPACITY = 10;

public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}

private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}

ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
modCount++;

// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}


我们来分析下此时调用ArrayList add方法的过程,此时通过上面我们可以明显看出来,首先它会先去检查此时数组的大小,ArrayList如果new出来的时候没有指定集合的大小或者指定的size小于10 那么第一次调用add方法的时候 会设置size为10 。接着如果此时集合的不够存储的话,那么集合会去扩容。从源码我们可以看出来ArrayList
扩容是在原来数组大小基础上增加1.5倍。用一个新的数组 复制旧数组数据。所以在平时代码的书写过程中,我们要特别注意下,如果使用ArrayList 的过程中,知道该申请多大的大小的数组 那么在new的过程中尽量写上去。 尽量避免数组的扩容。我们接着说,添加的时候,会指定数组下一个坐标的值为传入的参数。同时size,会+1。 我们调用arrlist.size()方法的时候,ArrayList 给出来的是真正集合的长度。如果new
的时候 给予了size为10  然后只添加了一个元素,那么此时的size还是只是1.但此时申请的数组长度是10.

 下面我们来看下 ArrayList的 contains的方法。我们有时候会判断一个集合是否存在某个值。分析下源码:

public boolean contains(Object o) {
return indexOf(o) >= 0;
}

public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}

从源码我们可以看出来,其实集合也是遍历数组,通过equals的方法去比对,如果存在返回数组的坐标,否则返回坐标-为1.再拿坐标跟0比较,小于0则返回false。

下面看下get方法。

public E get(int index) {
rangeCheck(index);

return elementData(index);
}

private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}


其实就是通过数组坐标获取值。在获取的时候会判断下坐标是否大于数组大小 大于则抛出角标越界异常。

关于remove方法。

public E remove(int index) {
rangeCheck(index);

modCount++;
E oldValue = elementData(index);

int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work

return oldValue;
}

public static native void arraycopy(Object src,  int  srcPos,
Object dest, int destPos,
int length);


从源码中可以得知,

说明:arraycopy  参数: src-源数组。 srcPos-源数组中的起始位置。 dest-目标数组。
destPos 目标数据中开锁位置  length要复制的长度

举个列子吧  比如集合有元素  0,1,2,3,4,5  那么remove坐标2   则先调用arraycopy方法得到0,1,3,4,5,5 然后将坐标5置 为null  --size,这样这个集合就remove掉了元素。

特别强调 --size 和size--是有区别的  例如:此处

elementData[--size] = null 变成 elementData[size--] = null


会抛出角标越界的异常。因为此时的size--坐标是6 。

--size 先减再赋值,size--先赋值再减

ArrayLsit还有好多方法 暂时就不说了 有兴趣的话大家可以自己去看源码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: