ArrayList解析下篇

108 阅读3分钟

上篇分析到了add()/addAll()方法,接下来继续分析一下其他的核心方法

首先来分析一下删除数据方法:remove(int index)/remove(Object o)

//用于判断下标是否越界
private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
public E remove(int index) {
		//先进行判断index是否越界
        rangeCheck(index);
		//删除会引起modCount的增加,具体作用在上篇介绍过了
        modCount++;
        //记录要被删除的数据
        E oldValue = elementData(index);
		
        //后面元素要移动的长度
        int numMoved = size - index - 1;
        //>0也就是不在末尾的情况,将elementData数组从index+1的位置开始拷贝到elementData中index位置开	始,长度为numMoved的数据
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //将末尾置为null方便GC
        elementData[--size] = null; // clear to let GC do its work
		//返回被删除的数据
        return oldValue;
    }
 //删除指定元素的删除方法,先找到索引位置在删除
public boolean remove(Object o) {
	//首先会判断要删除的元素是否为Null,比较特殊用==
        if (o == null) {
        //循环遍历寻找元素为null的位置Index,调用fastRemove方法
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
        //不为空则用equals循环比较找到
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    //删除成功
                    return true;
                }
        }
        //没有这个数据则返回false
        return false;
    }    
 //快速删除的一个方法,不会返回被删除的值,也不用判断是否下标越界,因为是根据index找到的再调用这个方法,则可以保证不会下标越界,这样可以提升一定的效率
private void fastRemove(int index) {
	//删除会引起modCount变量的增加
        modCount++;
        int numMoved = size - index - 1;
        //不在末尾的情况
        if (numMoved > 0)
        //将elementData数组index+1的位置开始复制到index开始的位置,长度为numMoved
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
         //方便GC
        elementData[--size] = null; // clear to let GC do its work
    }

上面的删除方法是删除单个元素的情况, Arraylist还提供了删除范围元素的情况

protected void removeRange(int fromIndex, int toIndex) {
		//不多解释
        modCount++;
        //删除之后,后面的元素要移动的长度
        int numMoved = size - toIndex;
        //将elementData数组从toIndex开始拷贝到fromIndex开始,长度为numMoved
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // 新数组长度
        int newSize = size - (toIndex-fromIndex);
        //将新数组长度之后的数据置为null,方便GC
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        //容量大小变为newSize
        size = newSize;
    }
    //删除指定集合中的所有元素
    public boolean removeAll(Collection<?> c) {
    	//判断是否为null,null会抛异常
        Objects.requireNonNull(c);
        //调用方法
        return batchRemove(c, false);
    }
    /**
    c:指定的集合
    complment:boolean类型
    true的时候删除不包含在c中的元素,也就是交集
    false的时候删除包含在c集合中的元素,也就是差集
    @return:boolean,是否删除成功
    */
    private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        //r:读指针  w:写指针
        int r = 0, w = 0;
        //用于判断是否修改
        boolean modified = false;
        try {
            for (; r < size; r++)
            	//指定集合c中如果包含elementData[r]而且complement==false,
                elementData[w++]存放的就是出去c中有的元素
                //为true的话则存放二者的交集
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
           //正常结束的话r==size,否则会抛出异常
            if (r != size) {
            //将elementData数组从r位置开始拷贝到w位置开始,长度为size-r
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                //更新一个还剩多少数据
                w += size - r;
            }
            //如果w==size则表示保留了全部数据,没有发生修改
            if (w != size) {
                //将数组从w位置之后的数据指向null,方便为gc
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                //修改的次数
                modCount += size - w;
                //size更新为w
                size = w;
                //修改成功返回true
                modified = true;
            }
        }
        //返回是否修改成功
        return modified;
    }
    //删除全部元素
    public void clear() {
        modCount++;

        //全部删除,置为null被GC
        for (int i = 0; i < size; i++)
            elementData[i] = null;
		//容量变为了0
        size = 0;
    }

接下来是get()/set()方法

//获取
public E get(int index) {
		//判断是否下标越界
        rangeCheck(index);
		//底层结构依旧是数组,直接根据索引返回值
        return elementData(index);
    }
//设置更新数据
public E set(int index, E element) {
		//判断是否越界
        rangeCheck(index);
		//存储旧数据
        E oldValue = elementData(index);
        //更新Index位置上的数据
        elementData[index] = element;
        //返回修改之前的数据
        return oldValue;
    }

其他的一些方法解释

//实例序列化的方法
private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
    	//定义一个期望modCount,防止序列化期间被修改
        int expectedModCount = modCount;
        //默认的序列化方法,但transient/static修饰的属性不会被序列化
        s.defaultWriteObject();

       //写入容量大小
        s.writeInt(size);

       //按序写出数据
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }
		//如果modCount发生了改变,会抛出并发修改异常
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

    
     //从反序列化中重构实例
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        //elementData初始化等于空数组
        elementData = EMPTY_ELEMENTDATA;

        //默认反序列方法,读入非transient/static修饰属性
        s.defaultReadObject();

       //读入集合长度,可以忽略
        s.readInt();  //ignore
		//如果容量>0
        if (size > 0) {
     		//计算需要的最小容量,详细见上篇
            int capacity = calculateCapacity(elementData, size);
            //检查集合的类型/大小
            SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
            //检查是否需要扩容
            ensureCapacityInternal(size);
	    //定义数组
            Object[] a = elementData;
          //按顺序读取数据存入a数组
            for (int i=0; i<size; i++) {
                a[i] = s.readObject();
            }
        }
    }

ArrayList就介绍到这吧,其他的方法如迭代方法/isEmpty()等等方法有兴趣的可以自行了解。感谢欣赏!