深入 理解ArrayList实现原理

5 篇文章 0 订阅
5 篇文章 0 订阅

基于jdk1.8的ArrayList原码分析

ArrayList的特点:

底层数据结构为数组

元素排列有序、可重复、可以为null

线程不安全

元素查找快,增删慢

扩容大小为:原先数组大小乘以1.5

ArrayList的成员变量

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     *  默认初始化数组的大小为10
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 用于空实例的共享空数组实例
     * 当调用int类型和 Collection<? extends E> 类型构造方法时,int为0或者Collection的大小为0时
     * 将EMPTY_ELEMENTDATA赋值给elementData
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 用于默认大小的空实例的共享空数组实例。
     * 调用无参构造函数时将DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给elementData
     * 将其与EMPTY_ELEMENTDATA区分开来,扩容时区分逻辑,第一次新增元素时,
     * 如果elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA则扩容为10。具体在扩容部分讲解。
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * ArrAyList底层的数据结构为数组,使用transient修饰时,表示elementData不会被序列化
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * 数组的大小
     */
    private int size;
    /**
     *最大数组容量
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
}

ArrayList的构造方法

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    /**
     *  int类型参数构造方法,传入初始化的数组大小 initialCapacity
     *  如果 initialCapacity大于0时,则new 一个大小为initialCapacity数组对象赋值给elementData
     *  如果elementData等于0时则将EMPTY_ELEMENTDATA空数组赋值给elementData,使用共享空数组减少      * 空数组创建,优化性能
     *  如果elementData小于0时则抛出异常       
     */
    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);
        }
    }

    /**
     * 无参构造器,将DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给elementData;使用共享空数组减少 空      * 数组创建,优化性能
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 将入参集合转化为数组赋值给elementData
     * 判断数组的大小是否为空,如果为空则将EMPTY_ELEMENTDATA赋值给elementData
     * 如果数组不为空,则判断数组是否为Object类型数组,如果不是则需要重新复制成Object数组
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();//转化为数组
        if ((size = elementData.length) != 0) {//非空集合
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)//是否转化为Object数组
                //非Object数组需要复制后重新赋值给elementData
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
}

ArrayList的扩容机制

  • ArrayList的扩容发生在向数组添加元素时
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA 空数组扩容时,数组所需容量大小为10
  • EMPTY_ELEMENTDATA空数组扩容时,以size+1为基础进行扩容
  • 每一次扩容时都需要将原先数组复制到新数组中
 public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 数组扩容的入口
        elementData[size++] = e;
        return true;
    }
 private void ensureCapacityInternal(int minCapacity) {
       //calculateCapacity 计算数组所需容量大小
       //ensureExplicitCapacity 确认是否需要扩容
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    //如果通过无参构造器实例化ArrayList对象,在第一次添加元素时,这里会返回数组需要的容量为10,否则返     //回数组需要的容量为minCapacity,即当前数组大小+1
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);//取较大值
        }
        return minCapacity;
        }
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // 当所需容量大于数组容量时则进行扩容
        if (minCapacity - elementData.length > 0)
            //扩容方法
            grow(minCapacity);
    }
 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容大小为原先容量的1.5倍
        if (newCapacity - minCapacity < 0)//比较扩容后的大小和所需容量大小进行比较
            newCapacity = minCapacity;//扩容后的大小小于所需容量,则扩容大小为所需容量
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 扩容后需要通过复制将原先的数组赋值到新数组中
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

ArrayList的三种遍历方式

public class ArrayListTest {

    public static void main(String[] args) throws Exception {

        ArrayList<Integer> list = new ArrayList(Arrays.asList(1, 2, 3, 4, 5));
        //第一种方式 for循环
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        //第二种方式 for each
        for (Object o : list) {
            System.out.println(o);
        }
        //迭代器
        Iterator it = list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

ArrayList的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(elementData, index+1, elementData, index,
           numMoved);//将指定下标后面一位到数组末尾的全部元素向前移动一个单位
        elementData[--size] = null; // 将数组最后一位赋值为空
        return oldValue;
    }
  • 场景应用:删除ArrayList中小于4的数据
public class ArrayListTest {

    public static void main(String[] args) throws Exception {

        //方法一:使用for循环,从后向前进行遍历
        // 不能从前向后循环,因为删除非末尾元素时,需要将指定下标后面一位到数组末尾的全部元素向前移动一个单位,此时会有部分元素无法遍历到,
        // 使用从前向后的遍历方式得到结果为[2,4,5],元素2没有被移除
        ArrayList<Integer> list1 = new ArrayList(Arrays.asList(1, 2, 3, 4, 5));
        for (int i = list1.size() - 1; i >= 0; i--) {
            //过滤掉小于4的数据
            if (list1.get(i) < 4) {
                list1.remove(i);
            }
        }
        System.out.println(list1);//[4,5]

        //方法二:使用removeIf(Predicate<? super E> filter)方法
        ArrayList<Integer> list2 = new ArrayList(Arrays.asList(1, 2, 3, 4, 5));
        list2.removeIf((val -> val < 4));
        System.out.println(list2);//[4,5]

        //方法三:使用while循环
        ArrayList<Integer> list3 = new ArrayList(Arrays.asList(1, 2, 3, 4, 5));
        int i = 0;
        while (i < list3.size()) {
            if (list3.get(i) < 4) {
                list3.remove(i);
            } else {
                i++;
            }
        }
        System.out.println(list3);//[4,5]

        //方法四:使用迭代器
        ArrayList<Integer> list4 = new ArrayList(Arrays.asList(1, 2, 3, 4, 5));
        Iterator<Integer> iterator = list4.iterator();
        while (iterator.hasNext()) {
            if (iterator.next() < 4) {
                //这里不能使用 java.util.ArrayList.remove(int)方法,因为ArrayList是非线程安全的会抛出java.util.ConcurrentModificationException异常,在增强for循环中同理,编译就会报错
                iterator.remove();
            }
        }
        System.out.println(list4);//[4,5]

        //方法五:使用java.util.ArrayList.removeAll方法
        ArrayList<Integer> list5 = new ArrayList(Arrays.asList(1, 2, 3, 4, 5));
        ArrayList<Integer> removeList = new ArrayList();
        for (Integer o : list5) {
            if (o < 4) {
                removeList.add(o);
            }
        }
        list5.removeAll(removeList);
        System.out.println(list5);//[4,5]

    }
}
  • ArrayList的trimToSize方法
//去除冗余的数组容量
public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的数组是一种线性数据结构,它由相同类型的元素组成并按照顺序存储在内存中。Java中的数组可以使用new关键字动态地创建,并且长度是固定的,一旦创建后就不能再改变它的大小。 Java中的ArrayList是一个动态数组,它可以自动扩容以适应元素的添加和删除。ArrayList实际上是基于数组实现的,它内部维护了一个Object类型的数组来存储元素。当ArrayList的容量不足时,它会根据一定的策略自动扩容数组的大小,以容纳更多的元素。 ArrayList实现原理主要包括以下几个方面: 1. 初始容量和扩容因子:当创建一个ArrayList时,可以指定一个初始容量和一个扩容因子。初始容量指定了ArrayList内部数组的初始大小,扩容因子指定了数组增长的比例。默认情况下,初始容量为10,扩容因子为1.5。 2. 自动扩容:当ArrayList内部数组的容量不足时,它会自动扩容。具体实现是创建一个新的数组,将原数组中的元素复制到新数组中,并将新元素添加到新数组中。 3. 随机访问:由于ArrayList是基于数组实现的,因此它支持随机访问。可以通过索引来快速访问元素,时间复杂度为O(1)。 4. 插入和删除:在ArrayList中插入和删除元素的时间复杂度取决于操作的位置。如果在末尾插入或删除元素,时间复杂度为O(1),否则需要将后面的元素都向后移动,时间复杂度为O(n)。 总之,ArrayList是Java中常用的动态数组,它的实现基于数组,并且支持随机访问、自动扩容等特性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值