util.ArrayList之源码分析

来源:互联网 发布:淘宝美工与平面设计 编辑:程序博客网 时间:2024/06/05 17:20

在写这篇文章之前我也看过其他人写的关于ArrayList的源码分析,但总感觉差点什么,有些地方也说的很含蓄,所以今天自己写下,一是加深对其的理解,二是方便其他人的学习理解。看源码分析源码有什么用呢?我们可以学习java语言开发者他们写代码的一些好的习惯和设计思想,我觉得这是最重要的。

声明:我jdk的版本是1.7

首先要说的就是类的创建:

public class ArrayList<E> extends AbstractList<E> //ArrayList<T>它继承了AbstractList<E>这个类,AbstractList是list接口的骨干实现,它提<pre name="code" class="java">//list接口的默认实现,也是泛型的,list接口定义了集合必须实现的方法,RandomAccess这个类它是一个标记接口,里面没人任何方法,实现它的主要
//原因我想可能是为了某种标记,实现Cloneable接口是为了Object中的clone方法,最后一个接口Serializable就是为了序列化和反序列化了,实现序列化的好处是便于网络传输        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{<pre name="code" class="java">//原因我想可能是为了某种标记,实现Cloneable接口是为了Object中的clone方法,最后一个接口Serializable就是为了序列化和反序列化了,实现序列化的好处是便于网络传输        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{


我们可以很明显的看出,ArrayList它是有泛型的,而且是可序列化的。

下面就要说它的构造方法了,ArrayList的构造方法共有三个,让我们一起来看看。

 public ArrayList(int initialCapacity) {//这是一个int型参数的构造方法,初始可是给定一个初始list的大小        super();        if (initialCapacity < 0)//初始大小的参数必须大于0            throw new IllegalArgumentException("Illegal Capacity: "+                                               initialCapacity);        this.elementData = new Object[initialCapacity];//elementData这是是定义的一个数组,这就是说arraylist的底层是数组实现的。    }    public ArrayList() {//无参的构造函数,可以看到默认初始化的大小空间为10        this(10);    }    public ArrayList(Collection<? extends E> c) {//这是接受一个参数为集合类型的构造函数        elementData = c.toArray();//把这个集合转换成数组的形式        size = elementData.length;//size是一个全局的变量,用来记录list集合的大小        // c.toArray might (incorrectly) not return Object[] (see 6260652)        if (elementData.getClass() != Object[].class)            elementData = Arrays.copyOf(elementData, size, Object[].class);//数组拷贝    }

说完了构造方法,那么在我的代码中已经有一个自己的对象了,接下来就是添加元素的方法了,

 public boolean add(E e) {//添加一个指定的泛型的元素,如果在创建集合的时候没有指定泛型,在添加的时候一般可以添加任意类型的数据        ensureCapacityInternal(size + 1);  // 这个方法主要是用来判断上次分配的空间是否够这次添加元素有使用,如果不够就扩容,下面会纤细看到这个方法。        elementData[size++] = e;//集合大小加1        return true;    }    public void add(int index, E element) {//往某个位置上添加元素        rangeCheckForAdd(index);//校验这个位置是否合法。方法在下面<span style="white-space:pre"></span>//下面的这些和add()方法一样,就不多做赘述了        ensureCapacityInternal(size + 1);  // Increments modCount!!        System.arraycopy(elementData, index, elementData, index + 1,                         size - index);        elementData[index] = element;        size++;    }    private void ensureCapacityInternal(int minCapacity) {        modCount++;        // overflow-conscious code        if (minCapacity - elementData.length > 0)//判断上次分配的空间是否够这次添加元素有使用            grow(minCapacity);//这个方法就是集合能够动态扩容的最主要的方法了,会在下面纤细的讲到    }
 private void rangeCheckForAdd(int index) {//这个方法很简单,主要是检查索引的位置是否大于0且小于集合的大小        if (index > size || index < 0)            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));    }
   private void grow(int minCapacity) {        // overflow-conscious code        int oldCapacity = elementData.length;//扩容前获取集合的大小        int newCapacity = oldCapacity + (oldCapacity >> 1);//定义新集合的大小,在旧大小的基础上进行移位运算,我记得在jdk1.6中这儿的处理是不一样的,是用了除2的计算方法,总的来说移位的运算效率会高点。        if (newCapacity - minCapacity < 0)//以防万一,做的一些处理            newCapacity = minCapacity;//        if (newCapacity - MAX_ARRAY_SIZE > 0)//MAX_ARRAY_SIZE  这是定义的一个final的int变量,也就是定义了一个int类型的最大值            newCapacity = hugeCapacity(minCapacity);        // minCapacity is usually close to size, so this is a win:        elementData = Arrays.copyOf(elementData, newCapacity);//数组拷贝    }<pre name="code" class="java">private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;  //定义了MAX_ARRAY_SIZE  是Integer类中定义的一个最大的常量-8而来的

下面在说一个addAll()方法,addAll()方法也有2个,一个是直接添加一个元素addAll(Objct obj),另一个是在某个索引位置添加一个元素addAll(int index,Objct obj).在这儿我主要分析一个,因为它和add()方法差不多,就多了把集合转换为数组的一个过程。

public boolean addAll(Collection<? extends E> c) {        Object[] a = c.toArray();//其他的地方和add方法差不多,唯一多了的就是先把集合转换成一个数组,基本上没有什么难度。        int numNew = a.length;        ensureCapacityInternal(size + numNew);          System.arraycopy(a, 0, elementData, size, numNew);        size += numNew;        return numNew != 0;    }
下面contain(),get(),indexOf(),lastIndexOf()几个方法放到一起分析,为什么要放到一起看完就明白了。

public E get(int index) {//get()方法是不是很简单,哈哈,先判断一下索引index是否存在,如果存在就直接从数组中取出来。        rangeCheck(index);        return elementData(index);    }public boolean contains(Object o) {//集合中是否包含某个元素,是调用了它自身的indexOf(Object o)这个方法        return indexOf(o) >= 0;    } public int indexOf(Object o) {//下面就让我们看看这个方法        if (o == null) {//list.add(null)  是合法的,所以说list中可以保存一个null的值            for (int i = 0; i < size; i++)                if (elementData[i]==null)                    return i;        } else {            for (int i = 0; i < size; i++)//下面就是调用equals方法了                if (o.equals(elementData[i]))                    return i;        }        return -1;    }  public int lastIndexOf(Object o) {        if (o == null) {            for (int i = size-1; i >= 0; i--)//和indexOf()方法唯一不同的就是遍历的时候是从后往前去遍历的,这样找到的就是最后的索引                if (elementData[i]==null)                    return i;        } else {            for (int i = size-1; i >= 0; i--)                if (o.equals(elementData[i]))                    return i;        }        return -1;    }

下面在来看一下清空和删除的方法

 public void clear() {//遍历集合,把每一个元素赋值为null,然后把集合的大小设置为0,这样就把一个集合清空啦。。。        modCount++;        for (int i = 0; i < size; i++)            elementData[i] = null;        size = 0;    } public E remove(int index) {//删除某个索引位置的元素        rangeCheck(index);//验证这个索引是否存在        modCount++;        E oldValue = elementData(index);//把索引的值取出来,在最后返回,这也就是为什么我们调用了remove()这个方法后会返回当前索引的值        int numMoved = size - index - 1;//size-index-1  的值刚好是index位置的上一个位置        if (numMoved > 0)            System.arraycopy(elementData, index+1, elementData, index,   //对两个数组中的内容合并                             numMoved);        elementData[--size] = null; // 集合大小减1        return oldValue;    } public boolean remove(Object o) {        if (o == null) {//这个和indexOf()方法一样,把null值单独拿出来处理            for (int index = 0; index < size; index++)//下面就是遍历数组找到这个元素                if (elementData[index] == null) {                    fastRemove(index);//这个方法其实和remove(int index)差不多,在下面可以看到                    return true;                }        } else {            for (int index = 0; index < size; index++)//非null值的处理                if (o.equals(elementData[index])) {                    fastRemove(index);                    return true;                }        }        return false;    } private void fastRemove(int index) {//这个方法和remove(int index)的作用差不多,就不多说明了        modCount++;        int numMoved = size - index - 1;        if (numMoved > 0)            System.arraycopy(elementData, index+1, elementData, index,                             numMoved);        elementData[--size] = null; // Let gc do its work    }

个人感觉说到这儿基本的方法都说了,而且经过这么一分析,你有没有感觉其实底层的代码也就那样,并没有我们想象的那个高大上的代码,可以学习他们写代码的风格,比如if的花括号省略的这种情况,代码结构化的好处。


0 0
原创粉丝点击