• java容器类:ArrayList


    ArrayList是刚接触java用的最多的一个容器类,它也有很多的特点:

    • 自动扩容 实例化一个ArrayList对象以后,可以放任意多的对象进去,而不像数组,使用前必须声明大小,超过这个大小就会抛出数组越界的异常.
    • 检索速度快 速度快是相较于链表.链表要找到一个指定对象必须要对整条链进行遍历.而ArrayList只要知道索引就能很快得出结果,不需要费时进行遍历.

    ArrayList的原理##

    ArrayList是List家族中的一员,继承了AbstractList.依赖于数组帮它存储数据,所以它本质上和数组没有区别,只是数组的升级版本.所以它可以像数组一样可以用索引来查找对象.
    那么它是怎样实现自动扩容?当然就要先看看add方法了.

    private transient Object[] elementData;//这是帮助ArrayList管理数据的数组对象
    
    private static final int DEFAULT_CAPACITY = 10;//默认的容量为10
    
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!! 这个方法中确认是否有必要扩容
        elementData[size++] = e;
        return true;
    }
    
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == EMPTY_ELEMENTDATA) {    //判断elementData对象是否为空
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//取minCapacity和DEFAULT_CAPACITY之间最大的数作为数组的容量
        }
    
        ensureExplicitCapacity(minCapacity);//执行下一步方法
    }
    
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
    
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)//如果要求扩充的容量是否大于当前数组的长度
            grow(minCapacity);
    }
    
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;//原先的容量oldCapacity 
        int newCapacity = oldCapacity + (oldCapacity >> 1);//将要扩充的容量newCapacity 是原来的1.5倍
        if (newCapacity - minCapacity < 0)//如果newCapacity 反而比指定的容量minCapacity要小,那么扩充大小根据minCapacity来定
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)//如果newCapacity已经超出了最大范围,那么根据minCapacity来判断大小,这一步基本上不用关心,其实就是在扩充至1.5倍和minCapacity大小中选择
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);//这是ArrayList为什么能够扩容的原因.
    }
    
    public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());//拷贝原来的数组对象到一个新的长度为newLength的数组中.可以再进一步查看copyOf方法.
    }
    
    //如果数组类型是Object数组,那么就构建一个newLength的Object数组
    //如果不是的话就构建一个对应类型的数组对象
    //将原来的数组从头开始,按照原生数组长度或者数组对象的最小值,复制到新的数组对象,并返回该对象.
    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
    
    • 在add方法中,当它达到能够存储的上限时,它存储的内容就会被复制到另外一个"更大尺寸"的数组中,并被这个新的数组取而代之.而它则被无情地抛弃,伴随着垃圾回收消失在内存中.
    • 这其中最关键的就是System.arraycopy(original, srcPos, copy, destPos,length);
    • 指定原始的数组original和要存放复制元素的对象copy,从original的srcPos位置开始复制length长度的元素到copy的destPos位置.
      除开add,还有更多的方法调用了这个方法
    • add(int index, E e) 在指定位置插入对象.将elementData的inxdex开始的元素复制到elementData的index+1的位置,变向将index以后所有的元素后移了一位.这样就能理所当然的让新对象占据index的位置了.System.arraycopy(elementData, index, elementData, index + 1,size - index);
    • remove(int index) 判断移除的对象是不是最后一个,如果不是,那么将该索引的右边的对象左移一位,最后去掉尾部重复的对象System.arraycopy(elementData, index+1, elementData, index, numMoved);
    • addAll(Collection<? extends E> c) 先得到c的数组对象a,将数组根据a的长度扩容,然后将a拷贝到elementData的尾部System.arraycopy(a, 0, elementData, size, numNew);
    • addAll(int index, Collection<? extends E> c) 将c的数组对象拷贝到指定index的位置,如果index不在尾部,还要将elementData内index位置的对象后移System.arraycopy(elementData, index, elementData, index + numNew,numMoved);最后才同样调用相同方法System.arraycopy(a, 0, elementData, size, numNew);
  • 相关阅读:
    Python中return self的用法
    多分类问题的交叉熵计算
    Python爬虫之足球小将动漫(图片)下载
    Sklearn中二分类问题的交叉熵计算
    TensorFlow.js入门(一)一维向量的学习
    MySql 流程控制经典案列讲解
    MySql 流程控制
    MySql 函数
    MySql 存储过程
    MySql 视图
  • 原文地址:https://www.cnblogs.com/itsrobin/p/5184664.html
Copyright © 2020-2023  润新知