ArrayList源码、实现源码

た 入场券 2023-07-05 13:21 71阅读 0赞

属性:

  1. //用于序列化的id
  2. private static final long serialVersionUID = 8683452581122892189L;
  3. //默认构造方法的容量
  4. private static final int DEFAULT_CAPACITY = 10;
  5. //空数组,构造方法中参数为0时使用--->new ArrayList(0)
  6. private static final Object[] EMPTY_ELEMENTDATA = {};
  7. //空数组,默认构造方法使用--->new ArrayList()
  8. private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  9. //临时存放元素的数组,使用transient修饰,不会被序列化
  10. transient Object[] elementData;
  11. //元素实际个数
  12. private int size;
  13. //数组能分配的最大内存,超出会OutOfMemoryError
  14. private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

构造方法:

  1. //构造方法一,传入容量
  2. public ArrayList(int initialCapacity) {
  3. if (initialCapacity > 0) {
  4. this.elementData = new Object[initialCapacity];
  5. } else if (initialCapacity == 0) {
  6. //添加元素时会扩容
  7. this.elementData = EMPTY_ELEMENTDATA;
  8. } else {
  9. throw new IllegalArgumentException("Illegal Capacity: "+
  10. initialCapacity);
  11. }
  12. }
  13. //构造方法二,不传入初始容量
  14. public ArrayList() {
  15. //当添加第一个元素时,扩容到默认大小10
  16. this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
  17. }
  18. //构造方法三,传入一个集合,将其转化为ArrayList
  19. public ArrayList(Collection<? extends E> c) {
  20. //集合转数组
  21. elementData = c.toArray();
  22. if ((size = elementData.length) != 0) {
  23. // 检查c.toArray()返回的是不是Object[]类型,如果不是,重新拷贝成Object[].class类型
  24. if (elementData.getClass() != Object[].class)
  25. elementData = Arrays.copyOf(elementData, size, Object[].class);
  26. } else {
  27. // 如果c的空集合,则初始化为空数组EMPTY_ELEMENTDATA
  28. this.elementData = EMPTY_ELEMENTDATA;
  29. }
  30. }

ArrayList的add(E e)方法

大致思路: 首先判断是否需要扩容,先需要计算该集合的容量,如果这个集合是空集合的话,给定默认的容量为10,如果不是空集合的话再进行扩容,新容量的大小为原容量的1.5倍。如果新容量的大小小于需要容量,则以需要容量为准。如果新容量的大小大于最大容量,则以最大容量为准,最终实现数组的拷贝。


  1. public boolean add(E e) {
  2. // 检查是否需要扩容
  3. ensureCapacityInternal(size + 1);
  4. // 把元素插入到最后一位
  5. elementData[size++] = e;
  6. return true;
  7. }
  8. private void ensureCapacityInternal(int minCapacity) {
  9. ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
  10. }
  11. //计算容量
  12. private static int calculateCapacity(Object[] elementData, int minCapacity) {
  13. // 如果是空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA,就初始化为默认大小10
  14. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
  15. return Math.max(DEFAULT_CAPACITY, minCapacity);
  16. }
  17. return minCapacity;
  18. }
  19. private void ensureExplicitCapacity(int minCapacity) {
  20. modCount++;
  21. if (minCapacity - elementData.length > 0)
  22. // 扩容
  23. grow(minCapacity);
  24. }
  25. private void grow(int minCapacity) {
  26. int oldCapacity = elementData.length;
  27. // 新容量为旧容量的1.5倍
  28. int newCapacity = oldCapacity + (oldCapacity >> 1);
  29. // 如果新容量发现比需要的容量还小,则以需要的容量为准
  30. if (newCapacity - minCapacity < 0)
  31. newCapacity = minCapacity;
  32. // 如果新容量已经超过最大容量了,则使用最大容量
  33. if (newCapacity - MAX_ARRAY_SIZE > 0)
  34. newCapacity = hugeCapacity(minCapacity);
  35. // 以新容量拷贝出来一个新数组
  36. elementData = Arrays.copyOf(elementData, newCapacity);
  37. }

add(int index, E element)方法

  1. public void add(int index, E element) {
  2. //检查是否越界
  3. rangeCheckForAdd(index);
  4. //检查是否需要扩容
  5. ensureCapacityInternal(size + 1);
  6. //将index及其后的元素向后移动一位,空出index的位置
  7. System.arraycopy(elementData, index, elementData, index + 1,
  8. size - index);
  9. //空出的位置存放加入的元素
  10. elementData[index] = element;
  11. //数量加一
  12. size++;
  13. }

addAll(Collection<? extends E> c)方法

  1. public boolean addAll(Collection<? extends E> c) {
  2. //集合转为数组
  3. Object[] a = c.toArray();
  4. int numNew = a.length;
  5. //检查扩容
  6. ensureCapacityInternal(size + numNew);
  7. System.arraycopy(a, 0, elementData, size, numNew);
  8. size += numNew;
  9. return numNew != 0;
  10. }

get(int index)方法

  1. public E get(int index) {
  2. // 检查是否越界
  3. rangeCheck(index);
  4. // 返回数组index位置的元素
  5. return elementData(index);
  6. }
  7. private void rangeCheck(int index) {
  8. if (index >= size)
  9. throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
  10. }
  11. E elementData(int index) {
  12. return (E) elementData[index];
  13. }

remove(int index)方法

  1. public E remove(int index) {
  2. // 检查是否越界
  3. rangeCheck(index);
  4. modCount++;
  5. // 获取index位置的元素
  6. E oldValue = elementData(index);
  7. // 如果index不是最后一位,则将index之后的元素往前挪一位
  8. int numMoved = size - index - 1;
  9. if (numMoved > 0)
  10. System.arraycopy(elementData, index+1, elementData, index, numMoved);
  11. // 将最后一个元素删除,帮助GC
  12. elementData[--size] = null; // clear to let GC do its work
  13. // 返回旧值
  14. return oldValue;
  15. }

总结

(1)ArrayList内部使用数组存储元素,当数组长度不够时进行扩容,每次加一半的空间,ArrayList不会进行缩容;

(2)ArrayList支持随机访问,通过索引访问元素极快,时间复杂度为O(1);

(3)ArrayList支持求并集,调用addAll(Collection<? extends E> c)方法即可;

(4)ArrayList支持求交集,调用retainAll(Collection<? extends E> c)方法即可;

(5)ArrayList支持求单向差集,调用removeAll(Collection<? extends E> c)方法即可;

(6)ArrayList有两种迭代器,支持向前向后遍历。

(7)ArrayList底层基于数组,所以除了迭代器,也可以使用循环遍历。

(8)ArrayList查询快,增删慢,线程不安全。

ArrayList查询快的原因是因为底层是数组,只需要获取数组下标即可获得元素,对于增删慢的原因是因为ArrayList是需要移动元素,导致增删慢。

发表评论

表情:
评论列表 (有 0 条评论,71人围观)

还没有评论,来说两句吧...

相关阅读

    相关 新手学__ArrayList

    前景 今天看了些集合的知识,感觉都是比较熟悉的那些,但是总觉得只知道怎么用,具体的细节部分我还是想去了解一下,根据别人前几年的博客,沿着这个思路去熟悉熟悉~一天一个或者两

    相关 ArrayList

      1.构造函数 无参的构造函数! 该无参的构造函数,默认构造默认容量为10的空数组 即:一个new ArrayList的时候,不管用不用都会占用10单位

    相关 ArrayList分析

    构造函数(有参和无参): 无参:有个被transient关键字修饰的elementData的Object类型长度为0的数组。 有参:参数的含义就是这个集合的含量,在Arra