java源码学习-ArrayList

ArrayList

ArrayList是一个被频繁使用的类, 本篇源码学习主要围绕着构造器/add/get/set/remove以及一些简单的api来学习, 首先得明白ArrayList的继承关系
在这里插入图片描述
在List中通过继承了Collection接口, 在iterable接口里定义了返回iterator的方法, 所以这就是List可以进行迭代遍历的原因

	//编译前
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        for(Integer i: list) {
            System.out.println(i);
        }
        list.remove(Integer.valueOf(1));
        System.out.println(list.get(0));
    }
	// 编译后
	public static void main(String[] args) {
        List<Integer> list = new ArrayList();
        list.add(1);
        list.add(2);
        Iterator var2 = list.iterator();

        while(var2.hasNext()) {
            Integer i = (Integer)var2.next();
            System.out.println(i);
        }

        list.remove(1);
        System.out.println(list.get(0));
    }

可以看见我们使用的for each被解语法糖之后就是用迭代器来进行迭代遍历的, 所以在我们使用for each对list中当前的迭代元素进行删除的时候就会抛出异常, 后面会对这个原因进行解析

构造器

	// 传入列表的初始大小
	public ArrayList(int initialCapacity) {
		// list中的元素是通过对象数组的形式储存, elementData就是一个对象数组
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
        	// 这里的EMPTY_ELEMENTDATA是被final修饰的一个静态变量, 一个空数组
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
	// 无参构造器, 赋值的也是一个空数组, 后面会根据这个来给初始容量
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
	// 传入一个Collection类型的变量, 上面可以看见ArrayList与Collection是有继承关系的
    public ArrayList(Collection<? extends E> c) {
    	// 直接将c转为数组赋值, toArray返回的是object[]
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
            	// 这里的copyOf会将elementData进行深拷贝并返回为Object[]类型
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

add

add操作其实就是数组操作, 但是其中含有数组扩容

    public boolean add(E e) {
    	// 判断容量是否足够
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
  	// 一个确认容量大小的方法
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
    	// 这里判断elementData是不是用无参构造器来构造的
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        	// 传入的大小与默认大小10相比, 取大
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    
    private void ensureExplicitCapacity(int minCapacity) {
    	// 这里传入的是calculateCapacity处理后的minCapacity
    	// add操作无论如何都会进入这个方法, 所以在这里对modCount进行自增
        modCount++;

        // 不需要扩容的时候minCapacity是一定小于等于数组容量的
        // 这里才是对初始数组进行扩容到初始容量10的操作
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    private void grow(int minCapacity) {
        // 扩容后的容量为扩容前容量的1.5倍
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 如果扩容后的容量依旧比传入的参数小, 就将扩容后的容量设置为传入参数
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 如果扩容后容量大于Integer.MAX_VALUE - 8, 依据入参的大小
        // 设置扩容后容量为Integer.MAX_VALUE - 8或者Integer.MAX_VALUE
        // 这里设置的MAX_ARRAY_SIZE之所以不是Integer.MAX_VALUE是因为List要有8字节存放自身大小
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 新数组将原先的元素浅拷贝至新的扩容后的数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

contains

contains的时间复杂度是O(n), 调用indexOf方法实现的

	public boolean contains(Object o) {
		// 不存在o就会返回-1, 所以就是false
        return indexOf(o) >= 0;
    }
    public int indexOf(Object o) {
    	// 如果是null就不能用equals方法来寻找, 所以得判断null的情况
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
	// 顺便看看lastIndexOf
    public int lastIndexOf(Object o) {
    	// 与indexOf是一样的, 只是遍历是从后往前的
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

clone

clone是一个浅拷贝, 因为copyOf的拷贝过程也只是引用拷贝, 并不是值拷贝
可以自定义一个对象来进行测试

    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            // 这里的copyOf产生了一个新的Object[], 但是其中的元素地址还是不变
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }
	// 可以看见最后的新数组赋值的用本地方法arraycopy实现的
    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
    public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

System.arraycopy实例演示

get

get方法稍微简单

    public E get(int index) {
    	// 检查下标有无越界
        rangeCheck(index);

        return elementData(index);
    }
    // 这里只做了上界判断
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    // 下界判断交给elementData
        E elementData(int index) {
        return (E) elementData[index];
    }

在jdk8中不对负数做判断的解释

/**
* Checks if the given index is in range. If not, throws an appropriate
* runtime exception. This method does not check if the index is
* negative: It is always used immediately prior to an array access,
* which throws an ArrayIndexOutOfBoundsException if index is negative.
*/

可以理解为获取elementData[index]的时候, 如果index是负数那就会直接抛出异常, 这个动作并不是在ArrayList中发起的, 而是jvm抛出的异常

set

    public E set(int index, E element) {
    	// 也只是做上界判断
        rangeCheck(index);
		// 在这里如果index是负数, 就会直接抛出异常, set失败
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

remove

    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);
		// 这里要对数组进行移动, numMoved是要移动的元素的个数
        int numMoved = size - index - 1;
        if (numMoved > 0)
        	// 将从index+1位置开始的NumMoved个元素复制到index开始
        	// 假设1,2,3,4,5删除2,移动后的数组为1,3,4,5,5
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 最后将数组最后一位设为null, 并且数组拥有的元素个数-1
        elementData[--size] = null; // clear to let GC do its work
		
        return oldValue;
    }
	// 这是删除一个对象
    public boolean remove(Object o) {
    	// 也要对o进行判空操作, 遍历寻找然后删除
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
    private void fastRemove(int index) {
    	// modCount的作用是记录修改了列表结构的操作发生了几次
        modCount++;
        // 这里操作跟根据下标删除一样
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

	// 顺便带个clear, 本质也是删除嘛
	public void clear() {
        modCount++;

        // clear to let GC do its work
        // 将数组中所有元素设置为空, 这里设空之后, 原先的元素就失去了引用链, 会被gc回收
        for (int i = 0; i < size; i++)
            elementData[i] = null;
		// 大小归零
        size = 0;
    }

removeAll

removeAll的作用就是将入参中的元素从this中除去

	public boolean removeAll(Collection<?> c) {
		// 对c进行一个判空
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }
    private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
        	// 如果元素不存在那么w和r都会+1保持距离, 如果这个元素存在, 那么r就会+1, w保持不变
        	// 这样就表示当前的w位置是需要被删除的, 可以用其他元素覆盖
        	// 举个例子:1,2,3,4,5删除1,3,4, 那么后面得到的数组就是2,5,3,4,5
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            // 会进入这个if的情况就是c.contains有异常抛出
            // 如果一切正常那么r是一定与size相等的
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            // w==size的情况就是没有对数组进行过移动操作, 也就是c中没有一个元素与this相同
            if (w != size) {
                // clear to let GC do its work
                // 上面的过程可以知道此时w之前的就是删除后的新数组, 之后的都是无用元素
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                // 此次一共进行了size-w次的移动操作
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        // c中如果有元素与this相同就会返回true 否则就是false
        return modified;
    }

subList

    public List<E> subList(int fromIndex, int toIndex) {
    	// 下标检测
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }
    static void subListRangeCheck(int fromIndex, int toIndex, int size) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        if (toIndex > size)
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        if (fromIndex > toIndex)
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                               ") > toIndex(" + toIndex + ")");
    }

在ArrayList里面有一个内部类SubList, 这个SubList的一切功能都与ArrayList相同
所以这里的SubList其实是不能强制转换为ArrayList的, 他们之间不存在继承关系, 所以我们一般都是使用List来获取ArrayList的SubList的.
这个SubList中的所有元素都与原先的List中的引用地址相同, 所以实质上他们是同一个List, SubList可以理解为一个List中的一个窗口, 而SubList只拥有这个窗口, 他可以修改窗口里面的值

后记

阅读完ArrayList源码之后, 明白了ArrayList的所有操作都是围绕着数组进行的, 其实也与数组无异, 无非就是一个可以动态扩容的数组罢了

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值