• ArrayList 源码阅读记录


    目录

    简介

    源码分析

    简介

    • 数组型链表,底层数据结构是数组
    • RandomAccess 接口的实现类,表明这是随机访问类型,在有index的情况下,访问的复杂度为O(1),插入和删除复杂度比较高O(n)
    • 不是线程安全的,效率肯定比线程安全的高,单线程情况下使用
    • 源码分析没有特别说明,都是基于JDK1.8的

    源码分析

    1、一些重要的常量和变量

    1. //默认的初始化容量为10
    2. private static final int DEFAULT_CAPACITY = 10;
    3. //空的数组对象 ,用于空实例的共享空数组实例
    4. private static final Object[] EMPTY_ELEMENTDATA = {};
    5. //和上面差不多,只是如果使用默认的构造方法,那么久使用这个数组对象
    6. private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    7. //不参加序列化的数组对象,数组缓冲区,长度就是ArrayList的容量
    8. transient Object[] elementData; // non-private to simplify nested class access
    9. //modCount 用来记录 ArrayList 结构发生变化的次数。
    10. //结构发生变化是指添加或者删除至少一个元素的所有操作,或者是调整内部数组的大小,仅仅只是设置元素的值不算结构发生变化
    11. protected transient int modCount = 0;

    2、一些重要的方法源码

    2.1、初始化的三个方法

        这里需要注意的是,如果预先知道数据的容量大小,那么就填写该容量大小,不然会使用默认的容量,当超过默认容量的时候,会触发扩容机制, 频繁的扩容会浪费资源。

    1. /**
    2. *用户自定义初始化容量的初始化方法
    3. */
    4. public ArrayList(int initialCapacity) {
    5. if (initialCapacity > 0) {
    6. this.elementData = new Object[initialCapacity];
    7. }
    8. //如果容量为0 ,使用默认的初始化容量 ,也就是 10
    9. else if (initialCapacity == 0) {
    10. this.elementData = EMPTY_ELEMENTDATA;
    11. }
    12. //容量 < 0 ,抛出异常
    13. else {
    14. throw new IllegalArgumentException("Illegal Capacity: "+
    15. initialCapacity);
    16. }
    17. }
    18. /**
    19. * 默认初始化容量,10
    20. */
    21. public ArrayList() {
    22. this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    23. }
    24. /**
    25. *复制另外的一个集合
    26. */
    27. public ArrayList(Collection c) {
    28. elementData = c.toArray();
    29. if ((size = elementData.length) != 0) {
    30. // c.toArray might (incorrectly) not return Object[] (see 6260652)
    31. if (elementData.getClass() != Object[].class)
    32. elementData = Arrays.copyOf(elementData, size, Object[].class);
    33. } else {
    34. // replace with empty array.
    35. this.elementData = EMPTY_ELEMENTDATA;
    36. }
    37. }

    2.2、add()的方法

        添加元素的时候先ensureCapacityInternal()方法来确保容量大小是否足够,如果新增的长度大于原有的,则发生扩容机制(grow()方法),扩容到原来的1.5倍

    1. //添加单个元素
    2. public boolean add(E e) {
    3. ensureCapacityInternal(size + 1); // Increments modCount!!
    4. elementData[size++] = e;
    5. return true;
    6. }
    7. private void ensureCapacityInternal(int minCapacity) {
    8. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    9. minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    10. }
    11. ensureExplicitCapacity(minCapacity);
    12. }
    13. private void ensureExplicitCapacity(int minCapacity) {
    14. modCount++;
    15. // overflow-conscious code
    16. if (minCapacity - elementData.length > 0)
    17. grow(minCapacity);
    18. }
    19. /**
    20. * 可分配的最大数组大小,这里还是预留了8个长度大小
    21. */
    22. private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    23. /**
    24. * 数组扩容
    25. */
    26. private void grow(int minCapacity) {
    27. // overflow-conscious code
    28. int oldCapacity = elementData.length;
    29. //扩容为原来的1.5倍
    30. int newCapacity = oldCapacity + (oldCapacity >> 1);
    31. //如果新容量还是比当前容量小,那么新容量 = 当前容量
    32. if (newCapacity - minCapacity < 0)
    33. newCapacity = minCapacity;
    34. //如果新容量比可分配的最大数组大小还大,那么调用hugeCapacity()方法
    35. if (newCapacity - MAX_ARRAY_SIZE > 0)
    36. newCapacity = hugeCapacity(minCapacity);
    37. // 进行数组复制
    38. elementData = Arrays.copyOf(elementData, newCapacity);
    39. }
    40. private static int hugeCapacity(int minCapacity) {
    41. if (minCapacity < 0) // overflow
    42. throw new OutOfMemoryError();
    43. //如果当前容量大于可分配的最大数组大小,那么设置为Integer.MAX_VALUE,否则设定为MAX_ARRAY_SIZE;
    44. return (minCapacity > MAX_ARRAY_SIZE) ?
    45. Integer.MAX_VALUE :
    46. MAX_ARRAY_SIZE;
    47. }

    2.3、序列化

        我当时很好奇,为什么ArrayList还单独写了序列化和反序列化的方法,既然这么写,肯定有些原因的。如果从性能上思考,ArrayList的容量是动态增加的,声明的时候容量过大,那么剩下的就初始化为null,如果全部序列化的话,难免会浪费空间,同时也增加了点性能消耗。所以这里他序列化,只序列化到size这个地方。

    1. /**
    2. * 序列化
    3. */
    4. private void writeObject(java.io.ObjectOutputStream s)
    5. throws java.io.IOException{
    6. // 判断前后元素数量是否超出
    7. int expectedModCount = modCount;
    8. s.defaultWriteObject();
    9. // 确定序列化大小
    10. s.writeInt(size);
    11. // 写入数据
    12. for (int i=0; i
    13. s.writeObject(elementData[i]);
    14. }
    15. if (modCount != expectedModCount) {
    16. throw new ConcurrentModificationException();
    17. }
    18. }
    19. /**
    20. * 反序列化
    21. */
    22. private void readObject(java.io.ObjectInputStream s)
    23. throws java.io.IOException, ClassNotFoundException {
    24. elementData = EMPTY_ELEMENTDATA;
    25. //获取序列化的大小
    26. s.defaultReadObject();
    27. // Read in capacity
    28. s.readInt(); // ignored
    29. if (size > 0) {
    30. // 根据大小而不是容量来分配数组
    31. ensureCapacityInternal(size);
    32. Object[] a = elementData;
    33. // 数据读出
    34. for (int i=0; i
    35. a[i] = s.readObject();
    36. }
    37. }
    38. }

    2.4、remove()方法

    调用 System.arraycopy() 将 index+1 后面的元素都复制到 index 位置上,该操作的时间复杂度为 O(N)

    1. /**
    2. *删除列表中指定位置的元素。
    3. *将任何后续元素向左移动(从它们的元素中减去1)
    4. */
    5. public E remove(int index) {
    6. //确认边界
    7. rangeCheck(index);
    8. modCount++;
    9. E oldValue = elementData(index);
    10. int numMoved = size - index - 1;
    11. if (numMoved > 0)
    12. System.arraycopy(elementData, index+1, elementData, index,
    13. numMoved);
    14. elementData[--size] = null; // clear to let GC do its work
    15. return oldValue;
    16. }
    17. /**
    18. * 删除列表中指定元素,遍历,然后获取index,调用fastRemove(),最后还是根据index来进行删除
    19. */
    20. public boolean remove(Object o) {
    21. if (o == null) {
    22. for (int index = 0; index < size; index++)
    23. if (elementData[index] == null) {
    24. fastRemove(index);
    25. return true;
    26. }
    27. } else {
    28. for (int index = 0; index < size; index++)
    29. if (o.equals(elementData[index])) {
    30. fastRemove(index);
    31. return true;
    32. }
    33. }
    34. return false;
    35. }
    36. /*
    37. * 私有删除方法,该方法跳过界限检查而不跳过
    38. * 返回删除的值
    39. */
    40. private void fastRemove(int index) {
    41. modCount++;
    42. int numMoved = size - index - 1;
    43. if (numMoved > 0)
    44. System.arraycopy(elementData, index+1, elementData, index,
    45. numMoved);
    46. elementData[--size] = null; // clear to let GC do its work
    47. }

  • 相关阅读:
    Spring Boot 中使用 Poi-tl 渲染数据并生成 Word 文档
    mybatis的工作原理
    在链表上实现 Partition 以及荷兰国旗问题
    Java基础—接口Lock
    架构师考试周报三
    时间同步NTP
    内存管理架构及虚拟地址空间布局
    数据治理要点
    【MATLAB第80期】基于MATLAB的结构核岭回归SKRR多输入单输出回归预测及分类预测模型
    ​Chrome插件:Postman Interceptor 调试的终极利器
  • 原文地址:https://blog.csdn.net/toward_south/article/details/127597479