前面一章节,我们介绍了集合的类图,那么本节将学习Collection 接口中最常用的子类ArrayList类,本章分为下面几部分讲解(说明本章采用的JDK1.6源码进行分析,因为个人认为虽然JDK1.8进行了部分改动,但万变不离其宗,仍然采用的JDK1.6的引子进行的优化,因此学会了1.6对于1.8也就理解了)。
一、ArrayList 的常见功能
在分析ArrayList的源码前,我们先看下ArrayList的常见的功能:
packagestudy.collection;importjava.util.ArrayList;importjava.util.Date;importjava.util.List;public classTestDemo01
{public static voidmain(String[] args)
{
List list= newArrayList();//ArrayList:底层实现时数组,线程不安全,效率高。所以,查询快。修改、插入、删除慢。//LinkedList:底层实现是链表,线程不安全,效率高。所以,查询慢。修改、插入、删除快。//Vector:线程安全的,效率低。
list.add("aaa");
list.add("aaa");
list.add(newDate());
list.add(newDog());
list.add(1234); //注意,list集合中只能添加引用类型,这里包装类的:自动装箱!
list.remove(new String("aaa"));
System.out.println(list.size());for(int i=0;i
System.out.println(list.get(i));
}
list.set(3, new String("3333"));
list.add(4, new String("3333"));
System.out.println(list.isEmpty());
list.remove(new Dog()); //hashcode和equals
System.out.println(list.size());
List list2= newArrayList();
list2.add("bbb");
list2.add("ccc");
list.add(list2);//跟顺序的操作
String str = (String) list.get(0);
System.out.println(str);
list.set(1, "ababa");
list.remove(0);
}
}classDog
{
}
从上述可以看到了,ArrayList 接口中除了继承自父类Collection 接口中的方法外,还实现了List接口中扩充的和索引相关的方法,这都源于其底层为数组结构。
二、ArrayList 的重要的属性
上面的部分列举了ArrayList中常见的一些功能方法,那么这些方法又是如何使用的呢,下面我们将进行源码的剖析,在剖析前,我们可以自己思考下,我们知道ArrayList 是一个动态扩展的集合,之所以动态扩展的原因或者说比数组强的地方肯定就在于数组的长度是固定的,不能扩展,这是数组的最大缺陷,所以才有了集合,那么ArrayList,那么其底层肯定也采用的是数组结构,因为它叫ArrayList嘛,那么其重要的属性之一,必然是定义了一个数组。如下:
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
{private static final long serialVersionUID = 8683452581122892189L;/*** The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer.*/
private transientObject[] elementData;/*** The size of the ArrayList (the number of elements it contains).
*
*@serial
*/
private int size;
ArrayList就是一个以数组形式实现的集合,其元素的功能为:
private transient Object[] elementData; //ArrayList是基于数组的一个实现,elementData就是底层的数组
private int size; //ArrayList里面元素的个数,这里要注意一下,size是按照调用add、remove方法的次数进行自增或者自减的,所以add了一个null进入ArrayList,size也会加1
三、ArrayList 的构造方法分析
在分析完上面的属性后,我们紧接着来看下ArrayList的构造方法:
/***构造一个具有指定容量的list*/
public ArrayList(intinitialCapacity) {super();if (initialCapacity < 0)throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);this.elementData = newObject[initialCapacity];
}/*** 构造一个初始容量为10的list,也就说当我们经常采用new ArrayList()的时候,实际分配的大小就为10*/
publicArrayList() {this(10);
}/***构造一个包含指定元素的list,这些元素的是按照Collection的迭代器返回的顺序排列的*/
public ArrayList(Collection extends E>c) {
elementData=c.toArray();
size=elementData.length;//c.toArray might (incorrectly) not return Object[] (see 6260652) 这里是因为toArray可能不一定是Object类型的,因此判断如果不是就进行了拷贝转换操作
if (elementData.getClass() != Object[].class)
elementData= Arrays.copyOf(elementData, size, Object[].class);
}
可以看到,ArrayList 提供了三个构造方法,分别的含义,已经注释到代码上面了,那么想一下指定容量的构造方法的意义,既然默认为10就可以那么为什么还要提供一个可以指定容量大小的构造方法呢?思考下,下面会说到。
四、ArrayList 的常见方法分析
1.add 添加元素
添加的方法,共有四个,下面我们分别分析下其功能源码:
/***添加一个元素*/
public booleanadd(E e)
{//进行扩容检查
ensureCapacity(size + 1); //Increments modCount!!//将e增加至list的数据尾部,容量+1
elementData[size++] =e;return true;
}/***在指定位置添加一个元素*/
public void add(intindex, E element) {//判断索引是否越界
if (index > size || index < 0)throw newIndexOutOfBoundsException