Java动态数组ArrayList实现动态原理

 更新时间:2023年08月10日 08:48:44   作者:很闲很快樂  
ArrayList是一种动态数组,它可以在运行时自动调整大小以适应元素的添加和删除,在Java中,你可以使用ArrayList类来实现动态数组,本文将给大家介绍一下ArrayList动态数组,是怎么实现动态的

简介

ArrayList是一个动态数组,可以通过泛型来决定数据中存放的元素类型

怎么实现的动态

之所以是动态数组,是因为在使用无参构造函数创建ArrayList之后,默认为空数组

public ArrayList() {  
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;  
}

在你每次去add一个元素的时候,通过ensureCapacityInternal方法去检查添加后元素的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,以满足添加数据的需求

public boolean add(E e) {
	// size为当前数据存放元素的数量
    ensureCapacityInternal(size + 1);
    // elementData为ArrayList内部存放数据的数组
    elementData[size++] = e;  
    return true;
}
private void ensureCapacityInternal(int minCapacity) { 
	// 判断数组是否为空,为空则将最小容量设置为默认值10
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {  
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);  
    }  
	// 判断当前容量是否满足最小容量
    ensureExplicitCapacity(minCapacity);  
}
private void ensureExplicitCapacity(int minCapacity) {
	// modCount用于记录修改次数,作用于fast-fail机制
	// 如果在迭代的时候发现modCount被修改了,则会抛出异常来避免可能会出现的不确定行为
    modCount++;  
    // 如果当前容量小于最小容量,则进行扩容
    if (minCapacity - elementData.length > 0)  
        grow(minCapacity);  
}
private void grow(int minCapacity) {  
    int oldCapacity = elementData.length;  
    // 计算新的容量,扩大为原容量的1.5倍, oldCapacity >> 1 运算表示右移一位,也就是相当于除以二并取整
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)  
        newCapacity = minCapacity; 
    // 如果新容量超过了数组的最大容量,调用hugeCapacity方法来获取一个更大的容量
    if (newCapacity - MAX_ARRAY_SIZE > 0)  
        newCapacity = hugeCapacity(minCapacity);  
    // 拷贝原数组,将原数组的元素复制到新数组中 
    elementData = Arrays.copyOf(elementData, newCapacity);  
}
private static int hugeCapacity(int minCapacity) {  
    if (minCapacity < 0) 
        throw new OutOfMemoryError();
	// 如果最小容量大于最大容量,则将容量设置为Integer.MAX_VALUE
    return (minCapacity > MAX_ARRAY_SIZE) ?  
        Integer.MAX_VALUE :  
        MAX_ARRAY_SIZE;  
}

从上面的源码解释可以看到,如果新建数据没有给定初始值,ArrayList是每次在进行add操作的时候去进行扩容的,扩容则会涉及到Arrays.copyOf深拷贝,会造成内存和性能的消耗,所以在生产项目中,推荐大家尽量在创建ArrayList对象的时候就指定其容量。

Fast-Fail机制

大家肯定注意到了上面提到的通过modCount实现的Fast-Fail机制

Fast-Fail是一种机制,用于检测在迭代过程中是否有其他线程对集合进行了修改。当一个线程在迭代 ArrayList 时,如果其他线程对 ArrayList 进行了结构性修改(如添加、删除元素),那么迭代器会立即抛出 ConcurrentModificationException 异常,以避免出现不确定的行为。 例子:

public class Example1 {  
    public static void main(String[] args) {  
        ArrayList<String> list = new ArrayList<>(3);  
        list.add("qwe");  
        list.add("asd");  
        list.add("zxc");  
        Iterator<String> iterator = list.iterator();  
        while (iterator.hasNext()){  
            String next = iterator.next();  
            System.out.println(next);  
            list.remove(next);  
        }  
    }  
}
输出:
qwe
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 com.example.lepractice.collection.Example1.main(Example1.java:21)

造成快速失败的常见操作包括:

  • 在迭代过程中,使用 ArrayList 的 add、remove、clear 等方法修改集合的结构。
  • 多个线程同时对 ArrayList 进行修改操作。

为了避免快速失败,可以采取以下措施:

  • 在迭代过程中,不要修改集合的结构。如果需要修改,可以使用迭代器的 remove 方法,对应上面的例子也就是iterator.remove()。
  • 在多线程环境下,对于需要并发修改的情况,可以使用线程安全的集合类,如 CopyOnWriteArrayList。

如果使用语法糖 for (String next : list) {
System.out.println(next);
list.remove(next);
} 这样的遍历方式,其实内部也是使用的迭代器,所以会存在同样的问题

到此这篇关于Java动态数组ArrayList实现动态原理的文章就介绍到这了,更多相关Java ArrayList实现动态内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java GZIPOutputStream流压缩文件的操作

    Java GZIPOutputStream流压缩文件的操作

    这篇文章主要介绍了Java GZIPOutputStream流压缩文件的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • Java中的Comparable和Comparator接口

    Java中的Comparable和Comparator接口

    这篇文章主要介绍了Java中的Comparable和Comparator接口,文章围绕主题展开详细的内容戒杀,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-09-09
  • Spring boot读取外部化配置的方法

    Spring boot读取外部化配置的方法

    大家好,本篇文章主要讲的是Spring boot读取外部化配置的方法,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-02-02
  • Java中线程休眠编程实例

    Java中线程休眠编程实例

    这篇文章主要介绍了Java中线程休眠编程实例,本文直接给出代码实例,并对休眠方法做了一番讲解,需要的朋友可以参考下
    2015-06-06
  • EasyExcel工具读取Excel空数据行问题的解决办法

    EasyExcel工具读取Excel空数据行问题的解决办法

    EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单,节省内存著称,下面这篇文章主要给大家介绍了关于EasyExcel工具读取Excel空数据行问题的解决办法,需要的朋友可以参考下
    2022-08-08
  • java搭建ftp/sftp进行数据传递的全过程

    java搭建ftp/sftp进行数据传递的全过程

    ftp是一种文件传输协议,让客户端和服务端能够互相传递文件,图片等数据,sftp也是一种文件传输协议,但是相比较而言要比ftp安全性更好些,但是也有缺点就是传输效率低
    2021-07-07
  • Junit测试多线程无法得到结果的问题解决

    Junit测试多线程无法得到结果的问题解决

    在测试一个文件转换工具类的时候,发生一个有趣的现象,同样的输入,使用Main函数可以正确解析,得到结果,使用Junit却无法得到结果,神奇的是,即使捕获Throwable,也无法捕获到仍和异常。
    2021-05-05
  • Eclipse+Webservice简单开发实例

    Eclipse+Webservice简单开发实例

    这篇文章主要介绍了Eclipse+Webservice简单开发实例的相关资料,需要的朋友可以参考下
    2016-02-02
  • Spring中TransactionSynchronizationManager的使用详解

    Spring中TransactionSynchronizationManager的使用详解

    这篇文章主要介绍了Spring中TransactionSynchronizationManager的使用详解,TransactionSynchronizationManager是事务同步管理器,监听事务的操作,来实现在事务前后可以添加一些指定操作,需要的朋友可以参考下
    2023-09-09
  • 详解Spring MVC的拦截器与异常处理机制

    详解Spring MVC的拦截器与异常处理机制

    这篇文章主要为大家详细介绍了Spring MVC的拦截器与异常处理机制,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-02-02

最新评论