Java源码分析(二)--ArrayList

概述:

ArrayList是一个重要的Java集合,底层基于数组实现,用以解决数组的一系列操作。


性质:

一、非同步

ArrayList与Vector类似,但与Vector不同的是,ArrayList未实现同步。

说明文档里提到:“This class is roughly equivalent to Vector, except that it is unsynchronized.”。

在Vector中,我们可以发现机会所有的方法都有synchronized修饰,而在ArrayList中你将找不到这种修饰。

对于非同步性质的相关原理及测试,可以查看我的上一篇文章


二、顺序容器,可以放入null元素

顺序容器自然不必说:元素存放的数据与放进去的顺序相同。

有趣的是,它可以放入null元素。而它对null元素的查找和删除等,是通过特处理来实现的,我们观察下面这个remove()方法的代码,可以很容易解释。

public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);//fastRemove在后面介绍
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

三、capacity容量 与 grow()

capacity代表实例的实际大小,容器内存储元素的个数不能多于当前容量。若容量不足,会通过grow()方法通过修改capacity来进行扩容。

grow()方法:

private void grow(int minCapacity){…},

Parameters:
    minCapacity the desired minimum capacity//所需的最小容量

这个grow方法,首先进行(1.5*oldCapacity)与(minCapacity)的比较,如果1.5倍的原容量已经足够,那会使用这个容量扩容,否则采用minCapacity进行扩容。

至于扩容的过程,其实是通过Array.copyOf()新建的对象,不是在原对象的废墟上加盖。

表现在程序上是这样:

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;

        /**(1.5*oldCapacity)与(minCapacity)的比较 */
        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);
    }

主要方法分析:

size(),isEmpty()

常数,直接与参数size进行操作比较

get(int Index),set(int Index,Object o)

常数,对index检查越界后,直接对对应坐标元素进行操作

get中值得注意的一点是:因为底层是以Object实现的,得到的元素进行了转换,才得到我们需要的类型:

return (E) elementData[index];//注意类型转换

IndexOf(Object o),lastIndexOf(Object o)

复杂度与目标位置有关

查找第一个值相等的对象( .equal() ),查找最后一个值相等的对象,返回坐标

区别在于遍历顺序

满足null元素查找。

add()

add方法有很多,不过原理都差不多,复杂度方面也与插入数目及位置有很大关系,但总归是线性的。

  1. 判断容量是否充足,试着通过前面介绍的grow()进行扩容。

  2. 通过Index进行计算,朝后复制,复制调用的是原生类System.arraycopy()。(有趣的是这个原生类可以自我复制)

  3. 进行覆盖填充

通过源码看一看:

public void add(int index, E element) {
        /** 检查坐标 */
        rangeCheckForAdd(index);

        /** 查看容量,并尝试扩容 */
        ensureCapacityInternal(size + 1);  // Increments modCount!!(这句是关于非同步的注释)

        /** 向后复制 */
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        /** 覆盖赋值 */
        elementData[index] = element;
        size++;
    }

具体来看:

  • add(Object o)直接加到最后

  • add(int Index,Object o)加到最后指定位置

  • add(Collection c) 直接对容器c进行了System.arraycopy(),将容器里所有元素,直接加到原elementData末尾。

  • add(int Index,Collection c)与上一个类似,不过指定了插入位置。

remove()

作为add()的逆操作,删除操作其实就是“向前复制”,但要想让GC起作用,还得手动将无效值,也就是最后一个位置赋null值。

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; //清除该位置的引用,让GC起作用
    return oldValue;
}

remove()方法有两个版本,
一个是remove(int index)删除指定位置的元素,返回删除的元素
另一个是remove(Object o)删除第一个满足o.equals(elementData[index])的元素。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值