《好好学习》之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();
    }
}
相关推荐
qq_17448285754 小时前
springboot基于微信小程序的旧衣回收系统的设计与实现
spring boot·后端·微信小程序
锅包肉的九珍5 小时前
Scala的Array数组
开发语言·后端·scala
心仪悦悦5 小时前
Scala的Array(2)
开发语言·后端·scala
2401_882727575 小时前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架
心仪悦悦5 小时前
Scala中的集合复习(1)
开发语言·后端·scala
代码小鑫6 小时前
A043-基于Spring Boot的秒杀系统设计与实现
java·开发语言·数据库·spring boot·后端·spring·毕业设计
真心喜欢你吖6 小时前
SpringBoot与MongoDB深度整合及应用案例
java·spring boot·后端·mongodb·spring
激流丶6 小时前
【Kafka 实战】Kafka 如何保证消息的顺序性?
java·后端·kafka
uzong7 小时前
一个 IDEA 老鸟的 DEBUG 私货之多线程调试
java·后端
飞升不如收破烂~8 小时前
Spring boot常用注解和作用
java·spring boot·后端