java - ArrayList源码窥视

        本文是基于JDK 1.8来查看的ArrayList的源码内容,不同版本的JDK,ArrayList部分的内容或有出入,但应该出入不大,可以相互参考借鉴。

        在ArrayList中,有6个基本属性,其中,关键的属性为表示元素数量的size(int类型),以及存储元素的elementData(Object[] 类型),ArrayList有3个构造器,两个有参和一个无参构造器,其内容如下:

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {   
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {            
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

        先说无参构造函数,是直接对存储元素的数组进行赋值,在源码中,查看DEFAULTCAPACITY_EMPTY_ELEMENTDATA,其为:private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 是一个缺省容量空元素数组。第一个有参构造函数,则直接说明了new 出一个相应空间大小的object数组,如果参数值为0,就将EMPTY_ELEMENTDATA赋值给elementData,其值等同于DEFAULTCAPACITY_EMPTY_ELEMENTDATA 。第二个有参构造函数,就是直接把一个泛型转成数组后赋值个elementData。

        再看add方法,每增加一个元素,size值就进行+1,在增长的地方,将元素填充到数组对应的位置。在如下代码可以看到,在数据增加之前,对数组是否要进行扩容进行了一系列的判断,如果确实发现,数组当前的容量不够满足了,遍对数组进行复制、扩容。

    public boolean add(E e) {
        ensureCapacityInternal(size + 1); 
        elementData[size++] = e;
        return true;
    }
    
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++; 
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    private void grow(int minCapacity) {        
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);        
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

        还有一个add(int index, E element)方法,在指定位置插入元素,在插入之前,先调用rangeCheckForAdd方法,对位置进行校验是否合法,然后调用ensureCapacityInternal方法,判断是否需要扩容,接着,对数组进行复制,将index后面的元素向后移动一位,最后,将index位置的元素覆盖上去以及size++;

    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

       另外的addAll方法也很简单,先把泛型转为object[]后,然后判断是否将容器扩容,最后调用复制的方法以及size的大小增加即可。

    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

       get和set就更为简单了,都是一个基于ArrayList里属性elementData的操作,操作前对位置进行一些校验即可。get直接从数组中取得位置为index的值,set就是将位置为index的值用element替换,并将原来的旧值返回。

    public E get(int index) {
        rangeCheck(index);
        return elementData(index);
    }

    public E set(int index, E element) {
        rangeCheck(index);
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

       remove操作和add操作较为相同,都是一个对数组的复制,remove之前,先对位置信息进行校验,然后调用复制的方法,最后将size-1的位置设置为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; 

        return oldValue;
    }

       clear操作更为直接,将每一处位置设值为null即可。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值