java集合之ArrayList

介绍

ArrayList是我们日常开发中最常用的集合类。继承自 AbstractList,实现了 List 接口。底层基于数组实现容量大小动态变化。允许 null 的存在。同时还实现了 RandomAccess、Cloneable、Serializable 接口,所以ArrayList 是支持快速访问、复制、序列化的。

属性

    //默认容量大小
    private static final int DEFAULT_CAPACITY = 10;
	//空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
	//默认无设置容量时空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
	//存放元素数组
    transient Object[] elementData; 
	//数组真实大小
    private int size;

构造方法

1、带初始容量大小的构造

    public ArrayList(int initialCapacity) {
    	//初始容量大于0,构造此容量大小的数组
    	//等于0,构造空数组
    	//小于0,抛异常
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

2、无参构造

    public ArrayList() {
    	//构造默认无设置容量时空数组
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

3、传入集合的有参构造

    public ArrayList(Collection<? extends E> c) {
    	//几个转数组
        elementData = c.toArray();
        //判断数组长度不等于0,判断elementData类型是否为Object[]
        //等于0则用空数组替换
        if ((size = elementData.length) != 0) {
        	//类型不为Object[],重新拷贝成Object[].class类型
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

添加元素

1、add (e)添加元素至数组最后

    public boolean add(E e) {
    	//检查是否需要扩容
        ensureCapacityInternal(size + 1); 
        //设置数组最后一位为e
        elementData[size++] = e;
        return true;
    }
    //检查是否需要扩容
    private void ensureCapacityInternal(int minCapacity) {
    	//数组为默认无设置容量时空数组,则
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        	//最小容量为默认容量10和传入的最小容量的大值
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //确保有明确容量,即有需要则扩容
        ensureExplicitCapacity(minCapacity);
    }
    //确保有明确容量,即有需要则扩容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        //需要的最小容量大于数组的长度,则扩容操作
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    //扩容
    private void grow(int minCapacity) {
        //原始数组的容量
        int oldCapacity = elementData.length;
        //扩容为原始数组的1.5倍
        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);
    }

2、add(i,e)添加原始至数组的指定位置

    public void add(int index, E element) {
    	//范围检查,是否在数组范围内
        rangeCheckForAdd(index);
        //检查是否需要扩容
        ensureCapacityInternal(size + 1); 
        //数组下标后面的元素往后移动一格位置
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        //设置指定下标的元素为element                
        elementData[index] = element;
        //数组大小+1
        size++;
    }
    private void rangeCheckForAdd(int index) {
    	//如果下标大于数组大小或者小于0,则抛异常
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

3、addAll(collection)添加集合至数组

    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;
    }

4、addAll(i,collection)添加集合至数组指定下标位置

    public boolean addAll(int index, Collection<? extends E> c) {
    	//数组范围检查
        rangeCheckForAdd(index);
        //集合转数组
        Object[] a = c.toArray();
        int numNew = a.length;
        //检查是否需要扩容
        ensureCapacityInternal(size + numNew); 
		//计算移动数量,集合大小减去指定下标
        int numMoved = size - index;
  		//移动数量大于0,则将此index位置之后的元素往后挪位,空出位置
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,numMoved);
		//将集合移动到index下标位置
        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

设置元素/获取元素

set设置元素

    public E set(int index, E element) {
    	//检查下标是否在范围内
        rangeCheck(index);
        //取出数组对应下标元素最终返回
        E oldValue = elementData(index);
        //设置数组下标的元素为新element
        elementData[index] = element;
        return oldValue;
    }

get获取

    public E get(int index) {
    	//检查下标是否在范围内
        rangeCheck(index);
        //返回数组下标对应元素
        return elementData(index);
    }

移除元素

1、remove(i)根据下标移除

    public E remove(int index) {
    	//检查下标是否在范围内
        rangeCheck(index);
        modCount++;
        //获取下标数组值
        E oldValue = elementData(index);
		//计算移动数量
        int numMoved = size - index - 1;
        //移动数量大于0,则将index后的元素往前移动一位
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //数组最后一位设为null
        elementData[--size] = null; 
		//返回移除位置原始值
        return oldValue;
    }

2、remove(o)根据元素移除

    public boolean remove(Object o) {
    	//元素为null
        if (o == null) {
        	//数组从头循环
            for (int index = 0; index < size; index++)
            	//找出第一个元素为null的下标,移除元素
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
        	//元素不为null,从数组头部循环,根据equals找出元素下标
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                	//移除元素
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
    //根据下标快速移除元素
    private void fastRemove(int index) {
        modCount++;
        //计算移动数量
        int numMoved = size - index - 1;
        //移动数量大于0,则将index后的元素往前移动一位
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //数组最后一位设为null
        elementData[--size] = null;
    }

交集

    public boolean retainAll(Collection<?> c) {
    	//集合c要求非空
        Objects.requireNonNull(c);
        return batchRemove(c, true);
    }
    
    //批量移除
    //complement为true表示删除c中不包含的元素
    //complement为false表示删除c中包含的元素
    private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        // 使用读写两个指针同时遍历数组
    	// 读指针每次自增1,写指针放入元素的时候才加1
        int r = 0, w = 0;
        boolean modified = false;
        try {
        	//循环数组
            for (; r < size; r++)
            	//判断传入集合c是否包含元素
                if (c.contains(elementData[r]) == complement)
                	//符合条件则写入elementData,w+1
                    elementData[w++] = elementData[r];
        } finally {
            //一般情况下r肯定会等于size,除非contains抛异常
            //若异常则把未读的元素都拷贝到写指针之后
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            //写指针不等于数组大小
            if (w != size) {
                //将写指针之后的数据设为null,方便GC
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }

总结

1、ArrayList内部使用数组存储元素,当数组长度不够时进行扩容,每次加一半的空间,不会进行缩容
2、ArrayList支持随机访问,通过索引访问元素极快
3、ArrayList添加元素到中间或者从中间删除元素比较慢,因为要移动后面元素

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值