• ArrayList 源码分析


    本质:就是动态数组,动态扩容
    1.内存地址连续,使用之前必须要指定数组长度
    2.可以通过下标访问的方式访问成员,查询效率高
    3.增删操作会给系统带来性能消耗[保证数据下标越界的问题,需要动态扩容]
    增删:总是要copy,移动元素,效率低
    查询:通过下标访问,效率很高

    一、构造器

    // 了解重要的基本属性 -- 方便理解源码
    
    // 默认的数组的长度-容量
    private static final int DEFAULT_CAPACITY = 10;
    // 空数组 - 
    private static final Object[] EMPTY_ELEMENTDATA = {};
    // 空对象 first element is added.
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    // 集合中实际存储数据的数组对象
    transient Object[] elementData;
    // 集合中元素的个数 
    private int size;
    
    // 无参构造器
    public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
            // this.elementData = {}
    }
    
    // 有参构造器
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            // 初始长度大于0 就创建一个指定大小的数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            // {}数组赋值给 this.elementData
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
    

    二、重要方法

    先分析add方法之后,在看看其他方法,基本就差不多啦,重点关注ensureCapacityInternal计算容量

    1. add+ensureCapacityInternal
    // add(E e)
    public boolean add(E e) {
        // 确定容量 动态扩容 size 初始 0
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 将要添加的元素 添加到数组中 elementData[0] = 1 --> size = 1
        elementData[size++] = e;
        return true;
    } 
    
    // 核心 -- 动态扩容
    // ensureCapacityInternal
        private void ensureCapacityInternal(int minCapacity) {
                ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
        }
        // calculateCapacity
        private static int calculateCapacity(Object[] elementData, int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                // 10  1 return 10
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            // 5
            return minCapacity;
        }
        //ensureExplicitCapacity
        private void ensureExplicitCapacity(int minCapacity) {
            modCount++; // 增长 操作次数
    
            // minCapacity 10
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
        // grow
        private void grow(int minCapacity) { // 10
            // overflow-conscious code
            int oldCapacity = elementData.length; // 0
            // newCapacity = 0
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity < 0)
                // newCapacity = 10 
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            // {}   {,,,,,,,,,} 返回一个新的数组 长度为10
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    
    1. 其他重要方法

      // add(int index, E element)
      public void add(int index, E element) {
          rangeCheckForAdd(index);
          ensureCapacityInternal(size + 1);  // Increments modCount!!
          System.arraycopy(elementData, index, elementData, index + 1,
                           size - index);
          elementData[index] = element;
          size++;
      }
      // remove
      public E remove(int index) {
          rangeCheck(index);
      
          modCount++;
          E oldValue = elementData(index);
      	// 获取要移动的元素的个数 {1,2,3,4,5,6,7,8,9} // 3  size=9  index=3
          //  {1,2,3,5,6,7,8,9,null}
          int numMoved = size - index - 1; // 5
          if (numMoved > 0)
              // 源数组 开始下标 目标数组 开始下标 长度
              System.arraycopy(elementData, index+1, elementData, index,
                               numMoved);
          elementData[--size] = null; // clear to let GC do its work
      	// 删除的节点对应的信息
          return oldValue;
      }
      
      public E get(int index) {
          // 检查下标是否合法
          rangeCheck(index);
      	// 通过下标获取数组对应的元素
          return elementData(index);
      }
      
      public E set(int index, E element) {
          rangeCheck(index); // 检查下标
      	// 获取下标原来的值
          E oldValue = elementData(index);
          elementData[index] = element;
          return oldValue;
      }
      

    三、自己的理解

    自己理解性的文字,以及需要掌握的重要代码

    初始化:
        new ArrayList( initialCapacity )
        
    //存储数据的对象 this.elementData
    // 无参构造区
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    
    // 参数 0
    this.elementData = EMPTY_ELEMENTDATA;
    // 参数 大于 0
    this.elementData = new Object[initialCapacity];
    
    add(E e){
        /** 
        	容量计算
        	ensureCapacityInternal
        	计算容量 calculateCapacity(size+1) //size 开始为 0 ,每次都会变化,等同于数组实际数据大小
        			- 无参  10以内  return 10 //默认给一个容量
        			       大于10  return size+1 
        			- 有参  return size+1 
       	    确保容量 ensureExplicitCapacity(size + 1)
        		     大于数组容量:扩容--每次扩容到原来的1.5倍 -- 通过位运算 -- 第一次传多少是多少,第二次							  才有可能出现扩容
        		     最大容量不超过 Integer.MAX_VALUE 
        		     	-- hugeCapacity 
        		     	 (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;                  新容量大小:newCapacity
          		     对象变化:this.elementData = Arrays.copyOf(elementData, newCapacity);
    	**/
    	elementData[size++] = e;
    }
    
    add(index,e){
          // add 只能是替换某个位置的元素,因为必须是 index < size
          // rangeCheckForAdd
          if (index > size || index < 0)
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    	  // arraycopy
           System.arraycopy(elementData, index, elementData, index + 1,
                             size - index);
    		// 赋值
            elementData[index] = element;
    }
    
    remove(index){
             // arraycopy
             System.arraycopy(elementData, index+1, elementData, index, numMoved);
             //  index 位置 给 null  -- GC          
             elementData[--size] = null; // clear to let GC do its work
    }
    
    // get 就是数组通过index操作
    get(index){
        return  elementData[index];
    }
    // set 就是数组通过index操作
    set(index){
        elementData[index] = element;
    }
        
    

    四、用例

    // 会抛出异常
    List arr = new ArrayList<>(5);
    arr.add(3,1);
    
    Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 3, Size: 0
    

    五、补充:FailFast机制

    ​ 快速失败的机制,Java集合类为了应对并发访问在集合迭代过程中,内部结构发生变化的一种防护措施,这种错误检查的机制为这种可能发生错误通过抛出 java.util.ConcurrentModificationException

    说白了就是list不能一边遍历一边修改

    // ArrayList有个内部类Itr实现了Iterator接口
    // ArrayList的每次操作modCount都会增加,如果另一个线程也做了修改,
    // 可能会导致modCount不等于expectedModCoun 
    //next()
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
        /**
         * 解决方式:
         * 使用Iterator的remove()方法
         * 使用for循环正序遍历 - 需要修正index ,不然会漏掉个别元素
         * 使用for循环倒序遍历
         */
    // 不能使用增强for循环-因为使用的就是内部迭代器iteratorhasNext和next()方法来判断和取下一个元素。
    
  • 相关阅读:
    分布式_理论_03_2PC
    分布式_理论_02_Base 理论
    分布式_理论_01_CAP定理
    分布式_理论_00_资源帖
    Git_学习_09_指定某些文件不上传
    Java_脚本引擎_03_nashorn支持es6
    Idea_学习_10_Idea远程debug
    Mybatis_总结_06_用_插件开发
    Mybatis_总结_05_用_Java API
    【BZOJ】2212: [Poi2011]Tree Rotations
  • 原文地址:https://www.cnblogs.com/laoyin666/p/13971867.html
Copyright © 2020-2023  润新知