集合篇(一)——ArrayList

集合篇(一)——ArrayList

ArrayList是一个在内存中连续分配的Object数组,
优点:在遍历元素和随机访问元素的效率高,因为是数组,访问时只需要根据下标进行查找就行,
缺点:在添加和删除元素时,就需要进行移动大量元素,在java中是利用Arrays.copyOf()方法以及System.arraycopy方法来进行移动,开销大

下面是ArrayList的源码

(一):重要属性

	//修改次数
    protected transient int modCount = 0;
    //链表大小
    private int size;
    //数组最大长度
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    //默认创建一个空集合
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];
    //存放元素的数组
    transient Object[] elementData;

底层是利用Object数组来存储的,在1.8中会利用一个空的Object数组来当做初始化,提供size来记录链表大小,提供了size()来获取这个size

(二)构造方法

	//ArrayList空参构造
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    //单参传入容量
    public ArrayList(int initialCapacity) {
    	//如果容量大于0
        if (initialCapacity > 0) {
        	//进行new一个Object数组
            this.elementData = new Object[initialCapacity];
        //如果容量为0,直接赋值为默认空的Object数组
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        //因为此时容量小于0了,所以抛出违规的容量提示
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

这里只先介绍常用的无参构造器以及单参传入大小构造器,无参构造器是将Object数组指向了我们默认的空Object数组,而单参则是判断我们的容量并且根据容量来选择分支

(三)常见方法
ensureCapacityInternal():判断是否需要进行扩容

	private void ensureCapacityInternal(int minCapacity) {
		//如果是默认的空Object数组,代表还没进行初始化,直接比较最大值,让较大的那个来作为容量
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //判断是不是需要进行扩容,
        ensureExplicitCapacity(minCapacity);
    }
	
	
	private void ensureExplicitCapacity(int minCapacity) {
		//修改次数++
        modCount++;
        // 如果我们需要的容量大于了我们的Object数组容量
        if (minCapacity - elementData.length > 0)
        	//调用grow方法来进行扩容
            grow(minCapacity);
    }

grow():扩容,重要

	private void grow(int minCapacity) {
        // 首先获取到旧容量
        int oldCapacity = elementData.length;
        //获取到1.5倍的旧容量,新容量=旧容量+旧容量/2  (PS:这边的 ‘>>‘ 代表是右移1位,位运算,代表除以2)
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果新容量还是比我们需要的容量小
        if (newCapacity - minCapacity < 0)
        	//直接让新容量=我们需要的容量
            newCapacity = minCapacity;
        //如果新容量大于了最大的数组长度
        if (newCapacity - MAX_ARRAY_SIZE > 0)
        	//调用方法,如果是大于最大的容量就返回Integer.Max_value
            newCapacity = hugeCapacity(minCapacity);
        // 调用Arrays.copyOf()方法来进行复制数组元素,将原来的元素放入到我们新创建的数组中
        //这就是扩容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

add(E e):添加元素

	public boolean add(E e) {
		//上面已经做了基本解析,不做讨论
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //这里就是将我们的元素赋值给Object数组
        elementData[size++] = e;
        //返回true,代表添加成功
        return true;
    }

add(int index,E e):在指定的位置添加元素

	public void add(int index, E element) {
		//
        rangeCheckForAdd(index);
        //判断是否需要进行扩容,上面已经讨论过
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //复制元素,主要就是将中间目的index处的地方空出来,将0-index不变,index-size移动到(index+1)-(size+1)处
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        //直接赋值
        elementData[index] = element;
        //长度++
        size++;
    }

get(int index):获取指定位置的元素

	public E get(int index) {
		//首先是检查下标是否违法
        rangeCheck(index);
        //直接返回指定下标的元素
        return elementData(index);
    }

set(int index,E element):设置元素

	public E set(int index, E element) {
		//检查范围,判断下标是否有问题
        rangeCheck(index);
        //先获取原来的元素,毕竟方法返回值是原数组下标元素
        E oldValue = elementData(index);
        //直接覆盖
        elementData[index] = element;
        //返回原数组元素
        return oldValue;
    }

remove(int index):移除指定下标元素

	public E remove(int index) {
		//依旧是检查元素下标是否违法
        rangeCheck(index);
        //修改次数++
        modCount++;
        //获取到旧值
        E oldValue = elementData(index);
        //获取到需要移动的个数,因为删除后,数组还是需要连续的,所以要将删除的元素空出来的部分用后面的补上
        int numMoved = size - index - 1;
        if (numMoved > 0)
        	//调用System.arraycopy来进行复制
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //元素前移了以后,最后一个没改变,需要把它变成空的
        elementData[--size] = null; // clear to let GC do its work
		//返回旧值
        return oldValue;
    }

以上就是ArrayList的部分源码了,总结就是底层采用Object数组,然后扩容是1.5倍,是采用的位运算操作,然后因为是操作数组,所以每次修改操作都需要进行检查下标的范围,很多地方用到了Array.copyof()以及System.arraycopy()方法

以上是个人见解,可能有误,勿喷

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值