zoukankan      html  css  js  c++  java
  • ArrayList源码分析

    一、定义

      ArrayList底层是由线性数组来实现的动态数组。新建一个ArrayList时,就是在底层新建一个线性数组。ArrayList的默认初始的数组长度为10(JDK1.8);

    二、关注点

      1、ArrayList集合的数据是可以为空的;

      2、数据有序(集合中数据的顺序和添加数据的顺序相同),从头向尾插入数据;

      3、线程不安全(多线程同时操作某一个指定集合时,共享数据存在线程安全 问题)

    三、ArrayList集合API

      1、新增元素

       1)在集合中追加元素   

     1 protected transient int modCount = 0;
     2 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
     3 public boolean add(E e) {
     4       //计数器
     5     modCount++;
     6       //新增元素操作
     7     add(e, elementData, size);
     8     return true;
     9 }
    10 
    11 private void add(E e, Object[] elementData, int s) {
    12   if (s == elementData.length){
    13     //数组已满,扩展数组
    14     elementData = grow();
    15   }
    16   //将E放置在数组索引为S的位置
    17   elementData[s] = e;
    18   //将整个数组的大小加1
    19   size = s + 1;
    20 }
    21 
    22 private Object[] grow() {
    23   //在原始数组长度的基础上加1
    24   return grow(size + 1);
    25 }
    26 
    27 private Object[] grow(int minCapacity) {
    28   //获取原始数组的长度,赋值给oldCapacity
    29   int oldCapacity = elementData.length;
    30   //判空操作(原始数组的长度大于0,或者该数组不为空数组)
    31   if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)    
    32     int newCapacity = ArraysSupport.newLength(oldCapacity,
    33                                 // minimum growth 按照上面代码逻辑差值是1
    34                                 minCapacity - oldCapacity,
    35                                 // preferred growth oldCapacity对2取余
    36                                 oldCapacity >> 1);
    37     return elementData = Arrays.copyOf(elementData, newCapacity);
    38   } else {
    39       //新建一个Object数组,长度为默认长度10和最小扩展量的最大值
    40     return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
    41   }
    42 }
    43 
    44 //扩容算法
    45 public static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
    46 public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
    47   //保证最小扩展量为1,因为minGrowth一直为1。若preGrowth大于minGrowth,则取preGrowth,否则为1;
    48   int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
    49   if (newLength - MAX_ARRAY_LENGTH <= 0) {
    50     //若扩展值小于MAX_ARRAY_LENGTH,则直接返回扩展值
    51     return newLength;
    52   }
    53   //当newLength的大小(最大扩容量)大于等于Integer.MAX-8的值后,使用该方法
    54   return hugeLength(oldLength, minGrowth);
    55 }
    56 
    57 private static int hugeLength(int oldLength, int minGrowth) {
    58   //获取最小扩增量1
    59   int minLength = oldLength + minGrowth;
    60   if (minLength < 0) { // overflow
    61     throw new OutOfMemoryError("Required array length too large");
    62   }
    63   //原始容量+1后的值(最小扩容量)小于等于integer最大值-8
    64   if (minLength <= MAX_ARRAY_LENGTH) {
    65     //返回Integer的最大值-8
    66     return MAX_ARRAY_LENGTH;
    67   }
    68   //最小扩容量大于Integre最大值-8时,直接返回Integrer的最大值
    69   return Integer.MAX_VALUE;
    70 }
    View Code

       2)在集合中指定索引位置添加元素  

     1 public void add(int index, E element) {
     2       //判断添加的索引是否在数组中
     3     rangeCheckForAdd(index);
     4     modCount++;
     5     final int s;
     6     Object[] elementData;
     7       //判断原始数组大小等于数组中元素的长度
     8     if ((s = size) == (elementData = this.elementData).length){
     9           //执行扩容操作
    10         elementData = grow();
    11     }
    12       //数组复制,将原数组(第一个elementData)从index的索引位置开始复制一系列元素A(长度为s-index)
    13       //复制到新数组(第二个elementData),将A从index+1的位置开始复制。完成后就将index位置空置出来
    14     System.arraycopy(elementData, index,
    15                      elementData, index + 1,
    16                      s - index);
    17       //将元素放置在index的位置
    18     elementData[index] = element;
    19       //将数组长度加1;
    20     size = s + 1;
    21 }
    22 
    23 private void rangeCheckForAdd(int index) {
    24   if (index > size || index < 0)
    25     throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    26 }
    View Code

         3)删除指定索引的元素  

     1 public E remove(int index) {
     2       //检查索引有没有越界
     3     Objects.checkIndex(index, size);
     4       //对数据做一个备份
     5     final Object[] es = elementData;
     6       //获取要删除的数据,用户结果返回
     7     @SuppressWarnings("unchecked") E oldValue = (E) es[index];
     8       //删除数组中数据
     9     fastRemove(es, index);
    10       //返回被删除的数据
    11     return oldValue;
    12 }
    13 
    14 private void fastRemove(Object[] es, int i) {
    15   modCount++;
    16   final int newSize;
    17   //判断被删除元素后面是否还有元素
    18   if ((newSize = size - 1) > i){
    19     //从原数组(第一个es)的第i+1索引处开始复制后面的所有元素
    20     //新数组(第二个es),将旧数组复制的元素在新数组中从i的索引位置开始复制元素到新数组
    21     System.arraycopy(es, i + 1, es, i, newSize - i);
    22   }
    23   //如果是最后一个元素或者执行完上一步的if语句,直接将最后一个索引的位置设置为null,方便GC回收内存
    24   es[size = newSize] = null;
    25 }
    View Code

       4)删除集合中指定的元素

     1 public boolean remove(Object o) {
     2     final Object[] es = elementData;
     3     final int size = this.size;
     4     int i = 0;
     5       //获取对应元素的索引值i
     6     found: {
     7         if (o == null) {
     8               //遍历整个数组,获取第一个元素为null的索引值i
     9             for (; i < size; i++)
    10                 if (es[i] == null)
    11                     break found;
    12         } else {
    13             for (; i < size; i++)
    14                   //获取第一个元素值为o的索引值i
    15                 if (o.equals(es[i]))
    16                     break found;
    17         }
    18           //集合中不存在该元素,直接返回false
    19         return false;
    20     }
    21       //删除指定索引的元素
    22     fastRemove(es, i);
    23     return true;
    24 }
    View Code

    四、ArrayLsit的优缺点

      1、优点

        1)ArrayList的查询元素的速度是非常快的,由于数组是由索引存在的,直接根绝索引查询即可(指哪打哪)。由于ArrayList实现了RandomAccess接口,所以ArrayList被标记为使用二分法进行查询(使用二分法找到指定的索引);

        2)ArrayList在最后的位置添加(删除)元素时,可以直接操作,时间复杂度为0(1);

      2、缺点

        在集合的指定位置(非最后的位置)进行添加和删除元素时,都要重新复制数组。增加空间复杂度O(n),比较耗费性能;

    五、为什么ArrayList集合中定义的数组使用elementData[],使用transient关键字进行修饰?

      由于ArrayList实现了Serializable接口,则该集合允许进行序列化和反序列化。对于未满的数组来说,真正需要序列化的只有那几个不为null的元素,其余的全部为null,序列化也没有意义。所以为了增强序列化(反序列化)的性能,使用transient来修饰,对数组不进行序列化,然后重写Serializable接口的WriteObject和ReadObject方法,使用重写的方法进行序列化和反序列化,保证只序列化或者反序列化存在的元素;

     1 @java.io.Serial
     2 private void writeObject(java.io.ObjectOutputStream s)
     3     throws java.io.IOException {
     4     // Write out element count, and any hidden stuff
     5     int expectedModCount = modCount;
     6       //先调用默认的序列化方法,序列化没有被Transient修饰的元素
     7     s.defaultWriteObject();
     8 
     9     // Write out size as capacity for behavioral compatibility with clone()
    10     s.writeInt(size);
    11 
    12     // Write out all elements in the proper order.
    13     for (int i=0; i<size; i++) {
    14           //只序列化存在的元素
    15         s.writeObject(elementData[i]);
    16     }
    17 
    18     if (modCount != expectedModCount) {
    19         throw new ConcurrentModificationException();
    20     }
    21 }
    22 
    23 @java.io.Serial
    24 private void readObject(java.io.ObjectInputStream s)
    25     throws java.io.IOException, ClassNotFoundException {
    26 
    27     // Read in size, and any hidden stuff
    28     s.defaultReadObject();
    29 
    30     // Read in capacity
    31     s.readInt(); // ignored
    32 
    33     if (size > 0) {
    34         // like clone(), allocate array based upon size not capacity
    35         SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, size);
    36         Object[] elements = new Object[size];
    37 
    38         // Read in all elements in the proper order.
    39         for (int i = 0; i < size; i++) {
    40             elements[i] = s.readObject();
    41         }
    42 
    43         elementData = elements;
    44     } else if (size == 0) {
    45         elementData = EMPTY_ELEMENTDATA;
    46     } else {
    47         throw new java.io.InvalidObjectException("Invalid size: " + size);
    48     }
    49 }
    View Code
    
    

     

  • 相关阅读:
    selenium.common.exceptions.WebDriverException: Message: 'chromedriver' executable needs to be in PATH.
    漫说996icu黑名单
    python datetime object 去除毫秒(microsecond)
    webpack4 系列教程(十四):Clean Plugin and Watch Mode
    webpack4 系列教程(十三):自动生成HTML文件
    webpack4 系列教程(十二):处理第三方JavaScript库
    webpack4 系列教程(十一):字体文件处理
    第一次遭遇云服务器完全崩溃
    music-api-next:一款支持网易、xiami和QQ音乐的JS爬虫库
    MathJax: 让前端支持数学公式
  • 原文地址:https://www.cnblogs.com/wwcxBlog/p/13093443.html
Copyright © 2011-2022 走看看