ArrayList源码详解

概述

ArrayList是我们日常写代码常用的集合之一,本文就 结合源码来介绍ArrayList常用方法

基本属性

 /**
     * 默认初始化容量
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 空实例数组
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 默认大小的空数组实例,在第一次进入ensureCapacityInternal()方法时,会初始化长度为10
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 存放元素的数组
     * transient关键词的作用 : 当前修饰的参数不会被序列化
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * 数组元素的数量
     */
    private int size;

构造方法

 /**
     * 带容量参数的构造方法
     * @param initialCapacity 初始容量大小
     */
    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() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

set()方法

 /**
     * 从数组中返回当前下标的元素
     * @param index
     * @return
     */
    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }
  /**
     * set方法,将元素设置到集合中
     * @param index 要设置到的下标
     * @param element 要设置的元素
     * @return
     */
    public E set(int index, E element) {
        rangeCheck(index); // 数组下标越界检查

        E oldValue = elementData(index); //直接根据index返回对应位置的元素(底层elementData是个数组)
        elementData[index] = element; // 将传入的element代替当前index的元素
        return oldValue; // 返回index位置原来的元素
    }

get()方法

/**
     * 获取当前下标的元素
     * @param index
     * @return
     */
    public E get(int index) {
        rangeCheck(index); // 数组下标越界异常检查

        return elementData(index); // 返回当前index的元素
    }

add()方法

    /**
     * 在指定index插入元素
     * @param index
     * @param element
     */
    public void add(int index, E element) {
        // 数组下标越界检查
        rangeCheckForAdd(index);

        // 将modCount+1,并校验添加元素后是否需要扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 将index位置及之后的所有元素向右移动一个位置(为要添加的元素腾出1个位置)
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        // 将当前index设置为element元素
        elementData[index] = element;
        // 容量+1
        size++;
    }
    
  /**
     * 为当前集合添加一个元素
     * @param e
     * @return
     */
    public boolean add(E e) {
        // 校验添加元素后是否需要扩容
        ensureCapacityInternal(size + 1);
        // 在数组尾部添加元素并将size+1
        elementData[size++] = e;
        return true;
    }

 	/**
     * 赋初始值以及校验是否需要扩容
     * @param minCapacity
     */
    private void ensureCapacityInternal(int minCapacity) {
        /**
         * 判断当前数组是否为DEFAULTCAPACITY_EMPTY_ELEMENTDATA
         * 如果是则将minCapacity设置DEFAULT_CAPACITY
         */
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        //判断添加元素后是否需要扩容
        ensureExplicitCapacity(minCapacity);
    }
    
      private void ensureExplicitCapacity(int minCapacity) {
        modCount++; // 修改次数+1

        // 数组扩容方法 当添加元素后的长度大于数组长度则进行扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
	 /**
     * 数组允许的最大长度
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

/**
     * 数组扩容方法
     * @param minCapacity
     */
    private void grow(int minCapacity) {
        // 原来的容量
        int oldCapacity = elementData.length;
        // 新容量 = 旧容量 + (旧容量 / 2)
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //当新数组长度小于minCapacity长度,则将新容量设为minCapacity
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 当新数组长度大于最大运行容量大
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            // 调用hugeCapacity()方法设置一个合适的容量
            newCapacity = hugeCapacity(minCapacity);
        // 将原数组元素拷贝到一个容量为newCapacity的新数组里面,底层调用的是 System.arraycopy()方法
        // 设置elementData为新数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

remove()方法

 /**
     * 通过下标删除元素
     * @param index
     * @return
     */
    public E remove(int index) {
        // 数组越界异常检查
        rangeCheck(index);

        // 修改次数+1
        modCount++;
        // 取出当前下标的值
        E oldValue = elementData(index);

        //  numMoved : 需要移动的元素的个数
        int numMoved = size - index - 1;
        // 当移动的下标大于 0 时,将当前数组从当前下标的后一个开始复制
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 将size-1,并将size-1位置的元素赋值为空(因为上面将元素左移了,所以size-1位置的元素为重复的,将其移除)
        elementData[--size] = null; // clear to let GC do its work
        // 返回被删除的元素
        return oldValue;
    }
     /**
     * 如果当前集合中存在一个与传入的参数相同的元素,则删除匹配到的第一个
     * 否则不变
     * @param o
     * @return
     */
    public boolean remove(Object o) {
        // 元素非空判断
        if (o == null) {
            // 如果入参元素为空,则遍历数组查找是否存在元素为空,如果存在则调用fastRemove将该元素移除,并返回true表示移除成功
            for (int index = 0; index < size; index++)
                // 当数组中有一个元素为null时,则调用快速删除
                if (elementData[index] == null) {
                    // 只有在删除对象时才会调用此方法,没有越界检查
                    fastRemove(index);
                    return true;
                }
        } else {
            // // 如果入参元素不为空,则遍历数组查找是否存在元素与入参元素使用equals比较返回true,如果存在则调用fastRemove将该元素移除,并返回true表示移除成功
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
     /**
     * 私有方法供上面的remove方法使用
     */
    private void fastRemove(int index) {
        // 修改次数+1
        modCount++;
        // 计算需要移动的元素的个数
        int numMoved = size - index - 1;
        //  如果需要移动的元素的个数>0,则将index+1位置及之后的所有元素,向左移动一个位置
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 将size-1,并将size-1位置的元素赋值为空(因为上面将元素左移了,所以size-1位置的元素为重复的,将其移除)
        elementData[--size] = null;
    }

clear()方法

 /**
     * 将集合中的元素全部删除,当前元素置为空
     */
    public void clear() {
        // 修改次数+1
        modCount++;

        // clear to let GC do its work
        // 遍历数组将所有元素置为空
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

ArrayList和LinkedList的比较

1、ArrayList的底层是动态数组实现的,LinkedList是基于链表实现的。
2、对于随机访问(get/set方法),ArrayList是通过下标直接定位到数组对应位置的节点,而LinkedList需要从头结点或尾结点开始遍历,直到找到目标结点。因此效率上ArrayList是要高于LinkedList的。
3、对于插入删除(add\remove方法),ArrayList需要移动目标结点后面的结点(通过System.arraycopy方法移动节点),而LinkedList只需要改变目标结点的前后节点的next或prev属性即可。因此在效率上,LinkedList要高于ArrayList。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值