PRivate transient Object[] elementData;private int size;protected transient int modCount = 0;(来之父类AbstractList,ArrayList不存在)这个elementData就是用来存放数据的object数组。size是数组的大长度,代表的是ArrayList的所存贮的数据的容量。modCount用来记录容器被改变的次数(对理解没什么用,后文不再解释这个变量)。然后我们看到的是ArrayList的构造器public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; }public ArrayList() { this(10); }这段代码是初始化ArrayList用的。从中我们可以看出,实例ArrayList有两种方法,而我们不一般不带参数的时候,ArrayList默认的大小是10.即产生一个容量为10的elementData的object数组。下一段代码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); } }这一段是为了确保数组可以存放下不停增多的数据,而专门设计的增加数组的长度的方法。从中 可以看出实现思路是:当需要存放数据到elementData数组中去的时候,会传入minCapcity(表示该数组应存入的总数据量),如果minCapcity大于现在的数组的长度,就产生一个新的数组,容量为以前的1.5倍+1,如果仍然小于,就直接将容量设置为minCapcity。然后再将之前存放在原数组的数据复制到新的数组中去,就实现了整个数组的扩容!看完前置准备的各种方法之后,可以看操作的方法了:size():public int size() { return size; }size就是elementData的长度。public boolean isEmpty() { return size == 0; }返回size==0的判断结果indexOf(Object o):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; }传入一个对象,返回其索引。 可以看出这个方法只是将传入的对象和elementData中的每个元素进行比较,然后再返回其数组下标号。找不到的话,就返回-1。contains(Object o)也只是调用了这个方法而已。lastIndexOf(Object o):public int lastIndexOf(Object o) { if (o == null) { for (int i = size-1; i >= 0; i--) if (elementData[i]==null) return i; } else { for (int i = size-1; i >= 0; i--) if (o.equals(elementData[i])) return i; } return -1; }可以看见lastIndexOf方法和indexOf是如此的相似,只是将遍历方向改变了一下就又多了一个方法,哈哈!get(index):public E get(int index) { RangeCheck(index); return (E) elementData[index]; }private void RangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException( "Index: "+index+", Size: "+size); }set(index,object)public E set(int index, E element) { RangeCheck(index); E oldValue = (E) elementData[index]; elementData[index] = element; return oldValue; }可以发现很多操作都只是在操作数组而已!毕竟ArrayList就是数组。add(o):public boolean add(E e) { ensureCapacity(size + 1); // Increments modCount!! elementData[size++] = e; return true; }add方法也是如此,只是此处需要判断是否要扩容add(index,object):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++; }在某个位置插入一个对象,这段代码难得也就这就话:System.arraycopy(elementData, index, elementData, index + 1, size - index); 其实这句好的意思是:将elementData从其第index和后面的元素,复制到elementData的第index+1和之后的位置,共复制size-index个元素,这样就是实现了将第index为元素腾空,让外界传进来的object占据这个宝座。这也是前面扩容的时候使用的是size+1而不是size的原因(确保数组复制时弄的size+1不会越界)remove(index):public E remove(int index) { RangeCheck(index); modCount++; E oldValue = (E) elementData[index]; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // Let gc do its work return oldValue; }可以发现和add是差不多的,而且还不许要考虑扩容的问题。同样的remove(object)相比起来,就多了一个判断该对象的index在哪,然后直接调用这个方法就行了。总结:
看到这里,想必大家对ArrayList也算有一个清晰地认知了。同时也明白了源码并不是很难,而且观看源码可以很好地加深我们对知识的理解,理解到底层。这样的学习会让我们的知识变得更加牢靠!由于今天实在是太晚了,所以没有贴上自定义实现的MyArrayList的代码(还没有写),所以今天只能告一段落了,若是大家还觉得不过瘾,想对容器有进一步的加深的欲望,希望能够继续观看我的博文。以后的文章我会逐一地对LinkedList,Map,Set,Tree等容器进行源码的解析,还希望大家能够多多支持,谢谢大家!如果有什么问题,还希望能够一起讨论,若是有什么不足之处,请一定要告诉我,谢谢
新闻热点
疑难解答