JAVA Collection 源码分析(一)之ArrayList
2014-07-31 22:58 西风狂诗曲 阅读(276) 评论(0) 编辑 收藏 举报到今天为止,差不多已经工作一年了,一直在做的是javaweb开发,一直用的是ssh(sh)别人写好的框架,总感觉自己现在高不成低不就的,所以就像看看java的源码,顺便学习一下大牛的思想和架构,read and write一直是提高自己编程水平的不二法门,写博客只是记录自己的学习历程,方便回顾,写的不好的地方,请多多包含,不喜勿喷,好了废话少说,现在让我们开始我们的历程把,Let's go!!!!!!!!
想看源码无从下手,不知道有没有跟我一样感觉的人们,今天用Intellij发现了可以找出类与类之间关系的图,可以让我们可以从上到下,各点击破。推荐给大家。
由于图片太大,我只是截取了一部分图,主要是关于我今天所要分析的ArrayList的类图,从图中我们可以看到最顶层的是Collection接口,Collection接口定义了基本的一些方法,所定义的方法在AbstractCollection中得到了具体的实现,包括size()啊,toArray()....许多基本的方法,大家要是有兴趣的可以去看看源代码。下面我们进入ArrayList中进行具体的分析。
直接上代码,我们从代码进行分析。
private static final long serialVersionUID = 8683452581122892189L; /** * 数组初始的容量 */ private static final int DEFAULT_CAPACITY = 10; /** * Shared empty array instance used for empty instances. */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 其实ArrayList中维护的就是一个Object数组 */ private transient Object[] elementData; /** * 这个size指的不是数组的长度,而是数组中所存储的元素的个数,因为他所采用的是数组动态增长策略,下面我们会看到他的
* 具体策略的实现。 * * @serial */ private int size;
接下来就是ArrayList的初始化,这个我就不贴源码了,public ArrayList(int initialCapacity)第一个初始化方法让你指定初始化容量的大小,public ArrayList(),
无参的初始化方法,他会默认将我们前面EMPTY_ELEMENTDATA = {}赋给elementData,这是我最常用的初始化方法,第三个初始化方法public ArrayList(Collection<? extends E> c)这是将一个实现了Collection接口的类的对象初始化,首先通过toArray方法将其转化成数组赋给elementData,我们直到elementData是一个Object数组,
所以它可以指向任何对象,接下来
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
这个很容易理解,如果当前的数组的Class不是Object,则将它转化为Object,按照我的理解就是重新开辟了空间然后让elementData指向它,不知道对不对,请清楚的可以指正我,我会进行修改的。好了,初始化分析完了,让我抽根烟整理整理思路先.....嘿嘿。
我们接着看
/** * 确保数组容量的策略,首先将数组长度在增加两倍,
* 然后与Integer.MAX_VALUE - 8 进行比较,如果大于 * 的话,则将数组长度増长至Integer.MAX_VALUE,否则就*/将其赋给Integer.MAX_VALUE-8,防止内存溢出 public void ensureCapacity(int minCapacity) { int minExpand = (elementData != EMPTY_ELEMENTDATA) // any size if real element table ? 0 // larger than default for empty table. It's already supposed to be // at default size. : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } private void ensureCapacityInternal(int minCapacity) { if (elementData == 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); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
接下来就是常用的一些方法的具体算法实现了,我就不一一举例了,我在这挑上几个我感兴趣的说一说:
public Object[] toArray() { return Arrays.copyOf(elementData, size); }
我们可以看到,在进行数组的操作的时候,大量的使用到了Arrays.copyOf工具方法,我为了一探究竟,跟踪了进去,看了一下源码,是最后发现其实是调用了一个叫做System.arraycopy的方法,在进去看到这个方法是一个native方法,有可能使用c或者c++写的,因为快吧,这是我的理解。
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; }
还有我们看到进行操作的时候大多数都有一个modCount++的操作,这是因为本身ArrayList不是线程安全的,所以在迭代的时候如果没有得到预期值的话,就会fast
fail.这个概念我有点模糊,决定去好好了解一下。
在进行新增的时候都会进行数组容量的扩展,防止数组出现越界的错误,这也是我们可以从中学习到的一点。
好了今天就先说这么多,其他的可以去看看源码,睡觉了。。。。。