《好好学习》之ArrayList

1. ArrayList 简介

ArrayList 的底层是数组队列,相当于动态数组,它的容量能动态增长,线程不安全。

ArrayList继承于 AbstractList ,实现了 List, RandomAccess , Cloneable, java.io.Serializable 这些接口。

ArrayList扩容是1.5倍机制:初始是10,第一次扩容后就是10+5

arduino 复制代码
/**
 * 首次初始化时,集合容量是10
 */
private static final int DEFAULT_CAPACITY = 10;

2.ArrayList --增

实例化时是个没有容量的空数组,首次调用add方法,容量扩容成默认容量10。

scss 复制代码
    public boolean add(E e) {
        //计数、扩容方法
        ensureCapacityInternal(size + 1);  
        //设值
        elementData[size++] = e;
        return true;
    }


    private void ensureCapacityInternal(int minCapacity) {
    	//首次新增会进入此if,获取初始化容量
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
    
    
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // 当真实数据数量大于容量时会触发下面扩容方法
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    
    private void grow(int minCapacity) {
        // 暂存旧的容量
        int oldCapacity = elementData.length;
        // 计算新的容量:10+10>>1 = 15
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // MAX_ARRAY_SIZE这个数值很大,正常业务代码中,很难触发这个if,可以不关注   
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 使用数组拷贝方法获得扩容后新数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

3.ArrayList --删

java 复制代码
// 根据值删除
public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}
	//具体解释看下面根据下标删除即可
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

//根据下标删除
    public E remove(int index) {
    //校验传入下标是否超过size(size代表数据具体数值数量)
        rangeCheck(index);

        modCount++;
        //获取当前传入的下标对应元素值
        E oldValue = elementData(index);
		//复制的位数
        int numMoved = size - index - 1;
        //大于0才会移动数组,正好等于0时代表你删除的是最后一位数据,所以也不需要移动数组
        if (numMoved > 0)
            /**
             * System.arraycopy的方法原型:调用此方法后结果就是index后的数据全部往前移动一位
             * @param src       要复制的源数组
             * @param srcPos    源数组要复制的起始位置(从0开始)
             * @param dest      要复制的目标数组
             * @param destPos   目标数组的起始位置(从0开始)
             * @param length    要复制的长度
            */
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // clear to let GC do its work 数值置为null,断开强引用,等待gc回收
        elementData[--size] = null; 

        return oldValue;
    }

4.ArrayList --查

scss 复制代码
public E get(int index) {
//校验传入下标是否超过size(size代表数据具体数值数量)
    rangeCheck(index);
//根据下标获取数组中对应的值返回
    return elementData(index);
}

5.ArrayList --非迭代器删除方法报错原因

php 复制代码
for (String a : list) {
    list.remove(a);
}

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
	at java.util.ArrayList$Itr.next(ArrayList.java:851)
	at TestString.main(TestString.java:22)
	

遍历时会调用内部类Itr->next方法

arduino 复制代码
final void checkForComodification() {
//此方法会校验modCount和expectedModCount一定要相同才能成功
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

而集合自带的删除方法,只会递增modCount,却不会更新expectedModCount,所以第二个元素删除时就会报错

我们需要调用迭代器里面的删除方法,就不会再报错,方法如下:

csharp 复制代码
public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        //重要的是这句代码,会同步modCount值给expectedModCount
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}
相关推荐
uzong2 小时前
技术故障复盘模版
后端
GetcharZp2 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程3 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研3 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi3 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国4 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy4 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
AntBlack5 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt
bobz9655 小时前
pip install 已经不再安全
后端
寻月隐君6 小时前
硬核实战:从零到一,用 Rust 和 Axum 构建高性能聊天服务后端
后端·rust·github