首页 > 学院 > 开发设计 > 正文

陌兮大魔王带你深入 学习ArrayListA(一)

2019-11-10 17:28:04
字体:
来源:转载
供稿:网友

前引

ArrayList的重要性就不言而喻了,在此鄙人就不多说了。本文适合新手阅读,如果是大神来访,还望能够帮忙发现小子知识不足之处,不甚感激。

ArrayList是什么?

ArrayList是什么?不就是容器吗,装个东西而已!如果你是这样回答的,那问:它底层到底是什么?你又如何作答?其实这时候,我们就因该深入其源码来学习了。一看到源码啊,许多新手肯定都是拒绝的。不外乎两种原因:1、代码看不懂,2、注释比代码更看不懂。所以,鄙人希望,我能给大家献点薄力,为大家的知识体系添砖盖瓦。

ArrayList(JDK1.6)源码:

其实很多源码都不难.而ArrayList的源码同样如此。现在就让我们走进ArrayList的源码世界(篇幅有限,挑一些常用的来说):进入ArrayList的源码中,首先看到的是这样一段代码
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等容器进行源码的解析,还希望大家能够多多支持,谢谢大家!如果有什么问题,还希望能够一起讨论,若是有什么不足之处,请一定要告诉我,谢谢大笑
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表