ArrayList 源码

0、transient
  • 被它修饰的元素不会被序列化
    使用另外的序列化方式,节省了空间(ArrayList的容量>=实际元素数量,如果直接序列化ArrayList的elementData[]元素数组,会把不含元素的空间也序列化,造成空间的浪费)
  • 这是一个用于存储ArrayList元素的缓冲数组,ArrayList的容量是数组的长度。
    任意一个空的,使用无参构造方法创建出来的ArrayList,第一次添加元素的时候,都要被扩充到默认初始化容量
    在这里插入图片描述
  • 序列化
    把ArraList实例以流的形式保存
 /**
     * Save the state of the <tt>ArrayList</tt> instance to a stream (that
     * is, serialize it).
     *
     * @serialData The length of the array backing the <tt>ArrayList</tt>
     *             instance is emitted (int), followed by all of its elements
     *             (each an <tt>Object</tt>) in the proper order.
     */
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // Write out element count, and any hidden stuff
        // 写出元素计数和其他隐式的属性,序列化期间被修改就会抛出异常
        int expectedModCount = modCount;
        s.defaultWriteObject();

        // Write out size as capacity for behavioural compatibility with clone()
        // 将实际元素个数,作为用于克隆的可信的容量
        s.writeInt(size);

        // Write out all elements in the proper order.
        // 依次写入元素
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }
        // 多线程修改数据,抛出异常
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

   
  • 反序列化
    从流里读出数据,重组ArrayList
 /**
     * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
     * deserialize it).
     */
     // 从流里读
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // 数组置空
        elementData = EMPTY_ELEMENTDATA;

        // Read in size, and any hidden stuff
        // 读入元素个数和其他隐藏属性
        s.defaultReadObject();

        // Read in capacity
        // 读入容量 ignore 说明这一步没啥用
        s.readInt(); // ignored
       // 就像克隆一样,给数组分配空间,依据实际元素值而不是容量大小
        if (size > 0) {
            // be like clone(), allocate array based upon size not capacity
            // 计算容量
            int capacity = calculateCapacity(elementData, size);
            SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
            //判断扩容
            ensureCapacityInternal(size);
           // 读取元素
            Object[] a = elementData;
            // Read in all elements in the proper order.
            for (int i=0; i<size; i++) {
                a[i] = s.readObject();
            }
        }
    }
1、 变量含义
  • int DEFAULT_CAPACITY 默认初始化容量 10
  • Object[] EMPTY_ELEMENTDATA 空数组
    构造方法带初始容量,但是初始容量为0
    在这里插入图片描述
    构造方法带集合参数,但是集合为空
    在这里插入图片描述
  • Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA 空数组
    只用于无参构造
    在这里插入图片描述
  • Object[] elementData 真正存储元素的位置
    (无参,创建的是空数组;有参,但是容量为0,创建的也是空数组,两个空数组只是变量名字不同,用于区分不同的构造情况,然后在第一次新增元素的时候采用不同的扩容方式)
    ArrayList arrayList = new ArrayList(); 对于这个 size是0,第一次add的时候,才变为10
 /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; 
  • int size 当前list内 含有的元素个数,注意和 容器容量区分开
    private static final int DEFAULT_CAPACITY = 10;
    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};
    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
   
2、在末尾新增元素以及扩容

先确保容量够用,再赋元素

     /**
     * Appends the specified element to the end of this list.
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

   private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

  • 先计算需要的最小容量 (也就是添加成功之后,实际元素个数)
    传入参数 minCapacity = size+1
    返回值 = 无参且第一次新增?max(minCapacity,default_Capicaty):minCapacity
   private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

  • modCount++(多线程操作,抛出异常),并判断 所需容量和数组实际长度的关系,来决定是否扩容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
  • 数组空间不够 ——> 扩容
    • newCapacity = oldCapacity+oldCapacity/2, 也就是1.5倍,然后进行边界判断:如果新容量还是不够用,直接使用minCapacity;如果新容量超过max_size,使用最大容量
    • 然后用新容量 拷贝数组(副本),然后赋给elementData
    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
3、在指定位置添加/删除元素

数组的两种复制方式 Arrays/System

  • Arrays.copyOfRange(源数组,起点,终点后一个位置)
    在这里插入图片描述
    在这里插入图片描述
    Arrays.copyof (源数组,复制长度) 基于System.arraycopy()
    新建了一个数组副本,最后返回副本
    在这里插入图片描述在这里插入图片描述
    复制长度超过源数组,默认值来凑(int --0 String–null)
    在这里插入图片描述
  • System.arraycopy(源数组,源数组起始位置,目标数组,目标数组起始位置,复制长度)
    目标数组必须已经存在–可以是自己 原地修改
    在这里插入图片描述
    在这里插入图片描述

正文

  • 先判断要添加元素的位置 是不是合规
    在这里插入图片描述
  • 再判断是否需要扩容
  • 然后进行数组复制
 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++;
    }

  • 删除也差不多 走流程 然后原地复制 最后返回被删除的元素(如果是boolean,删除成功返回true,失败返回false)
    在这里插入图片描述
4、其他方法
  • get 直接调用数组获取元素的方法 O(1)
    在这里插入图片描述在这里插入图片描述
  • toArray 返回的是副本
    在这里插入图片描述
  • 清空 遍历 赋予null O(n)
    在这里插入图片描述
  • indexOf 遍历,比值 O(n)
    在这里插入图片描述
    在这里插入图片描述
根据原作 https://pan.quark.cn/s/459657bcfd45 的源码改编 Classic-ML-Methods-Algo 引言 建立这个项目,是为了梳理和总结传统机器学习(Machine Learning)方法(methods)或者算法(algo),和各位同仁相互学习交流. 现在的深度学习本质上来自于传统的神经网络模型,很大程度上是传统机器学习的延续,同时也在不少时候需要结合传统方法来实现. 任何机器学习方法基本的流程结构都是通用的;使用的评价方法也基本通用;使用的一些数学知识也是通用的. 本文在梳理传统机器学习方法算法的同时也会顺便补充这些流程,数学上的知识以供参考. 机器学习 机器学习是人工智能(Artificial Intelligence)的一个分支,也是实现人工智能最重要的手段.区别于传统的基于规则(rule-based)的算法,机器学习可以从数据中获取知识,从而实现规定的任务[Ian Goodfellow and Yoshua Bengio and Aaron Courville的Deep Learning].这些知识可以分为四种: 总结(summarization) 预测(prediction) 估计(estimation) 假想验证(hypothesis testing) 机器学习主要关心的是预测[Varian在Big Data : New Tricks for Econometrics],预测的可以是连续性的输出变量,分类,聚类或者物品之间的有趣关联. 机器学习分类 根据数据配置(setting,是否有标签,可以是连续的也可以是离散的)和任务目标,我们可以将机器学习方法分为四种: 无监督(unsupervised) 训练数据没有给定...
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值