《好好学习》之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();
    }
}
相关推荐
Q_19284999069 分钟前
基于Spring Boot的九州美食城商户一体化系统
java·spring boot·后端
ZSYP-S38 分钟前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
Yuan_o_1 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
程序员一诺2 小时前
【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】
后端·python
DT辰白2 小时前
如何解决基于 Redis 的网关鉴权导致的 RESTful API 拦截问题?
后端·微服务·架构
thatway19893 小时前
AI-SoC入门:15NPU介绍
后端
陶庵看雪3 小时前
Spring Boot注解总结大全【案例详解,一眼秒懂】
java·spring boot·后端
Q_19284999063 小时前
基于Spring Boot的图书管理系统
java·spring boot·后端
ss2733 小时前
基于Springboot + vue实现的汽车资讯网站
vue.js·spring boot·后端
一只IT攻城狮4 小时前
华为云语音交互SIS的使用案例(文字转语音-详细教程)
java·后端·华为云·音频·语音识别