关于ArrayList的十三连问

news2024/11/27 18:44:14

文章目录

  • 一、底层存储结构是什么
  • 二、初始容量
  • 三、构造方法
  • 四、扩容原理
  • 五、读写速度比较
  • 六、克隆为深克隆还是浅克隆
  • 七、多线程环境下是否安全
  • 八、增强遍历时添加或删除元素会发生什么事情
  • 九、为什么数组被transient修饰
  • 十、通过`subList()`获得的集合能否转为`ArrayList`
  • 十一、使用`SubList`时有哪些注意事项
  • 十二、如何将集合转为数组
  • 十三、使用`Arrays.asList`有什么注意事项

推荐往期文章: ArrayList源码解读

一、底层存储结构是什么

答:数组

transient Object[] elementData;

二、初始容量

答:默认初始容量为10,也可以通过构造方法指定初始容量

  • 默认初始容量为10

    private static final int DEFAULT_CAPACITY = 10;
    
  • 通过构造方法指定初始容量

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
    

三、构造方法

答:三个

  • 无参构造方法

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
  • 指定初始容量的构造方法

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
    
  • 指定集合的构造方法

    public ArrayList(Collection<? extends E> c) {
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            // replace with empty array.
            elementData = EMPTY_ELEMENTDATA;
        }
    }
    

四、扩容原理

当需要向ArrayList集合中添加元素时,采用先扩容,后插入的方式。

ArrayList的扩容逻辑由grow()方法实现,扩容后底层数组的新长度=原长度*1.5,即容量扩展为原来的1.5倍,该计算方法是通过位运算实现的,且最大长度不得超过int的最大值,即 2 23 − 1 2^{23}-1 2231

当确定新的数组长度后,通过Arrays.copyOf获取一个具有新长度的数组,该方法底层是通过调用jvm本地方法System.arraycopy()方法实现的。

五、读写速度比较

答:读数据快,写数据慢。由于底层采用数组作为存储元素的结构,而数组支持根据下标读取元素。

根据下标操作ArrayList时,读取和修改操作快添加和删除操作慢(因为这两个操作需要移动数组中的元素)。特殊情况是在数组尾部添加元素时,操作快。

根据元素操作ArrayList时,读取、修改、添加、删除四个操作都慢,因为数组本身只支持根据下标操作元素,因此当根据元素操作时,需要从头遍历数组。

六、克隆为深克隆还是浅克隆

答:浅克隆

ArrayList实现了Cloneable接口并实现了clone()方法,因此具有克隆的功能。但是其clone()方法的实现中仅克隆了ArrayList对象,却没有克隆其内部数组中的元素(通过Arrays.copyOf()方法),结果是克隆后的ArrayList对象与克隆前的ArrayList对象为两个对象,内部数组中的元素却是相同的对象,指向同一个内存地址。

public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

代码演示如下:

public static void cloneDemo() {

    ArrayList<Person> personArrayList = new ArrayList<>();
    personArrayList.add(new Person("张三"));
    personArrayList.add(new Person("李四"));
    personArrayList.add(new Person("王五"));
    System.out.println("克隆前person集合,内存地址:" + System.identityHashCode(personArrayList) + ",元素:" + personArrayList);

    Object personListClone = personArrayList.clone();
    ArrayList<Person> newPersonList = (ArrayList<Person>) personListClone;
    System.out.println("克隆后person集合,内存地址:" + System.identityHashCode(newPersonList) + ",元素:" + newPersonList);

    newPersonList.get(0).setName("我修改了名字");
    System.out.println("修改元素==============");


    System.out.println("克隆前person集合,内存地址:" + System.identityHashCode(personArrayList) + ",元素:" + personArrayList);
    System.out.println("克隆后person集合,内存地址:" + System.identityHashCode(newPersonList) + ",元素:" + newPersonList);
}

输出:

在这里插入图片描述

七、多线程环境下是否安全

答:多线程不安全

因为在ArrayList中没有使用synchronized或其他加锁操作,因此线程不安全。

在多线程环境下,可以通过以下方法避免线程不安全的问题:

  • 在操作ArrayList的方法或代码块上加锁,无论是通过synchronized还是Lock
  • 使用Vector
  • 使用CopyOnWriteArrayList

八、增强遍历时添加或删除元素会发生什么事情

答:会抛出ConcurrentModificationException异常,但仅限于增强型遍历forEach()方法。

如下代码所示

private ArrayList<Integer> list = new ArrayList<>();

for(Integer i : list) {
    list.remove(i);
}

在遍历时通过添加或删除元素会导致其底层数组结构发生变化,ArrayList通过使用expectedModCountmodCount两个变量判断结构是否发生变化,如果有变化,则将抛出ConcurrentModificationException异常。

解决办法:

  • 使用ArrayList内部类迭代器ItrListItr提供的add()remove()方法对集合进行添加和删除操作。在迭代器的方法中会控制expectedModCountmodCount两个变量的值,使其避免抛出ConcurrentModificationException
  • 使用CopyOnWriteArrayList

九、为什么数组被transient修饰

答:使用关键字transient来修饰数组并不表示不将其序列化,ArrayList在序列化时,最主要的是将其内部元素进行序列化而不是数组,再加上数组中可能存在大量空元素,若将数组进行序列化,对时间和空间无疑都是一种消耗。

另外我们可以通过源码看一下他的序列化和反序列化过程。

  • 序列化

    在序列化时,通过遍历数组中的元素,将元素进行序列化,而非数组。

    private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{
        // Write out element count, and any hidden stuff
        int expectedModCount = modCount;
        s.defaultWriteObject();
    
        // Write out size as capacity for behavioural compatibility with clone()
        s.writeInt(size);
    
        // Write out all elements in the proper order.
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }
    
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }
    
  • 反序列化

    private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
        elementData = EMPTY_ELEMENTDATA;
    
        // Read in size, and any hidden stuff
        s.defaultReadObject();
    
        // Read in capacity
        s.readInt(); // ignored
    
        if (size > 0) {
            // be like clone(), allocate array based upon size not capacity
            int capacity = calculateCapacity(elementData, size);
            SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
            ensureCapacityInternal(size);
    
            Object[] a = elementData;
            // Read in all elements in the proper order.
            for (int i=0; i<size; i++) {
                a[i] = s.readObject();
            }
        }
    }
    

十、通过subList()获得的集合能否转为ArrayList

答:不能

subList()方法返回的是ArrayList的内部类SubList的实例,并不是 ArrayList 本身,而是 ArrayList 的一个视图,对于 SubList 的所有操作最终会反映到原列表上。因此无法转为ArrayList

public List<E> subList(int fromIndex, int toIndex) {
    subListRangeCheck(fromIndex, toIndex, size);
    return new SubList(this, 0, fromIndex, toIndex);
}

在《阿里巴巴Java开发手册》嵩山版中是这样描述的:

在这里插入图片描述

十一、使用SubList时有哪些注意事项

SubListArrayList的一个内部类,表示为ArrayList集合的一个视图,对子集合的操作会影响父集合,对父集合的操作也会影响子集合。

  • 修改父集合元素的值,会影响子集合
  • 修改父集合的结构,会引起子集合在遍历、增加、删除操作中出现ConcurrentModificationException异常
  • 修改子集合元素的值,会影响父集合
  • 修改子集合的结构,会影响父集合

在《阿里巴巴Java开发手册》嵩山版中是这样描述的:

在这里插入图片描述

十二、如何将集合转为数组

答:使用toArray()方法或toArray(T[] a)方法。

但是一般来说,我们都使用toArray(T[] a)方法实现,且传入的是类型完全一致、长度为0的空数组。至于为什么,在《阿里巴巴Java开发手册》嵩山版中是这样描述的:

在这里插入图片描述

十三、使用Arrays.asList有什么注意事项

答:使用Arrays.asList()方法得到的List实例为ArrayList 实例,但需要注意的是,此处返回的ArrayList 实例为Arrays的内部类,其全限定类路径为java.util.Arrays.ArrayList,而我们常用的ArrayList 的全限定类路径为java.util.ArrayList,这是两个不同的类。

当我们通过Arrays.asList()方法得到一个集合时,只能对其进行读取、遍历、修改操作,而不能使用修改集合结构相关的方法如增加、删除等,否则会抛出异常UnsupportedOperationException

且该内部类体现的是适配器的设计模式,其内部依然是一个数组,只是通过asList()方法改变我们操作数组的方式而已。

在《阿里巴巴Java开发手册》嵩山版中是这样描述的:

在这里插入图片描述



纸上得来终觉浅,绝知此事要躬行。

————————————————我是万万岁,我们下期再见————————————————

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/867046.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于灰狼优化(GWO)、帝国竞争算法(ICA)和粒子群优化(PSO)对梯度下降法训练的神经网络的权值进行了改进(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【python】一文了解Python爬虫 | 文末送书

目录 引言 1. 爬虫基础知识 1.1 什么是爬虫 1.2 HTTP协议 1.2.1 HTTP请求方法 1.GET请求 1.2.2 请求头常见字段 1.2.3 响应状态码 1.3 HTML解析 1.3.1 Beautiful Soup 解析库 1.3.2 XPath xpath解析原理: xpath 表达式 2. 爬虫进阶技巧 2.1 防止被反爬虫 2.1.1 …

信息论基础知识

注意&#xff1a;本文只针对离散随机变量做出探讨&#xff0c;连续随机变量的情况不适用于本文探讨的内容&#xff01; &#xff08;一&#xff09;自信息 1. 自信息 I ( x ) − l o g n P ( x ) \color{blue}I(x) - log_{n}{P(x)} I(x)−logn​P(x) 注意&#xff1a; 若n …

用ChatGPT和六顶帽思考法帮助自己更好地决策和解决问题

当我们在解决复杂问题时&#xff0c;我们常常陷入单一视角的状态。创造性思维领域的先驱爱德华德博诺&#xff0c;提出了六顶帽思考法[1]&#xff0c;这意味着我们可以从六个不同的视角来思考一个问题&#xff0c;以实现高水平决策和解决问题。 每一顶“帽子”代表不同的视角。…

【Matlab】PSO优化(单隐层)BP神经网络算法

上一篇博客介绍了BP-GA&#xff1a;BP神经网络遗传算法(BP-GA)函数极值寻优——非线性函数求极值&#xff0c;本篇博客将介绍用PSO&#xff08;粒子群优化算法&#xff09;优化BP神经网络。 1.优化思路 BP神经网络的隐藏节点通常由重复的前向传递和反向传播的方式来决定&#…

UGUI源码深度剖析

源码下载后 直接嵌入工程&#xff0c;删除引擎extension里的&#xff1b; 自制UI&#xff0c;在一个空场景中显示一个图片&#xff0c;当鼠标点击图片&#xff0c;执行操作。 gameobject &#xff1a; mesh meshfilter meshrender maintexture meshcollider camera ray

基于SSM的智能商城购物系统

基于SSM的智能商城购物系统 项目简介项目获取开发环境项目技术运行截图 项目简介 该智能商城系统主要实现两大功能模块:前台管理和后台管理 前台管理包括五大模块:用户登录注册、商品信息、购物车信息、个人信息管理、下单与订单管理、订单物流设置。 (1)用户登录注册模块:该功…

【Python机器学习】实验10 支持向量机

文章目录 支持向量机实例1 线性可分的支持向量机1.1 数据读取1.2 准备训练数据1.3 实例化线性支持向量机1.4 可视化分析 实例2 核支持向量机2.1 读取数据集2.2 定义高斯核函数2.3 创建非线性的支持向量机2.4 可视化样本类别 实例3 如何选择最优的C和gamma3.1 读取数据3.2 利用数…

大数据Flink(六十一):Flink流处理程序流程和项目准备

文章目录 Flink流处理程序流程和项目准备 一、Flink流处理程序的一般流程

使用jasypt对Spring Boot配置文件中的配置项加密

在Spring Boot中&#xff0c;有很多口令需要加密&#xff0c;如数据库连接密码、访问第三方接口的Token等。常见的方法就是用jasypt对口令进行加密。 实际上&#xff0c;jasypt可以对配置文件中任意配置项的值进行加密&#xff0c;不局限于对密码的加密。 1.在pom.xml中添加ja…

21 | 朝阳医院数据分析

朝阳医院2018年销售数据为例,目的是了解朝阳医院在2018年里的销售情况,通过对朝阳区医院的药品销售数据的分析,了解朝阳医院的患者的月均消费次数,月均消费金额、客单价以及消费趋势、需求量前几位的药品等。 import numpy as np from pandas import Series,DataFrame impo…

友善之臂NanoPi NEO利用fbtft驱动点亮1.69寸ST7789V2屏幕

屏幕介绍 本文以中景园1.69寸LCD&#xff0c;驱动芯片ST7789V2该款屏幕示例&#xff0c;屏幕的分辨率为240*280 屏幕引脚说明 NanoPi NEO IO介绍 屏幕与板子的IO连接关系 屏幕NanoPi NEOGNDGNDVCC3.3VSCLPC2SDAPC0RESPG11DCPA1CSPC3BLKPA0 下载交叉编译器和linux内核源码并按教…

c语言——完数的计算

完数即所有因子之和等于其本身值 列入&#xff0c;28124714&#xff0c;28所有的因子为1&#xff0c;2&#xff0c;4&#xff0c;7&#xff0c;14 而这五个因子之和恰好也是28. //完数的计算 /*完数即所有因子之和等于其本身值 列入&#xff0c;28124714&#xff0c;28所有的…

LeetCode700. 二叉搜索树中的搜索

700. 二叉搜索树中的搜索 文章目录 [700. 二叉搜索树中的搜索](https://leetcode.cn/problems/search-in-a-binary-search-tree/)一、题目二、题解方法一&#xff1a;迭代方法二&#xff1a;递归 带main函数测试用例 一、题目 给定二叉搜索树&#xff08;BST&#xff09;的根节…

在线状态监测如何使冷却塔维保管理受益

工业冷却塔作为关键的热交换装置&#xff0c;在许多生产流程中发挥着重要作用。为了保持其高效稳定的运行&#xff0c;实施连续的冷却塔状态监测变得至关重要。本文将以PreMaint设备数字化平台为例&#xff0c;探讨为什么建议采用远程冷却塔状态监测&#xff0c;以及如何借助振…

PHP证券交易员学习网站mysql数据库web结构apache计算机软件工程网页wamp

一、源码特点 PHP证券交易员学习网站 是一套完善的web设计系统&#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 下载地址https://download.csdn.net/download/qq_41221322/88205549 PHP证券交易员…

Python爬虫的requests(学习于b站尚硅谷)

目录 一、requests  1. requests的基本使用  &#xff08;1&#xff09;文档  &#xff08;2&#xff09;安装  &#xff08;3&#xff09;响应response的属性以及类型  &#xff08;4&#xff09;代码演示 2.requests之get请求  3. requests之post请求  &#x…

emqx-5.1.4开源版使用记录

emqx-5.1.4开源版使用记录 windows系统安装eqmx 去官网下载 emqx-5.1.4-windows-amd64.zip&#xff0c;然后找个目录解压 进入bin目录,执行命令启动emqx 执行命令 emqx.cmd start使用emqx 访问内置的web管理页面 浏览器访问地址 http://localhost:18083/#/dashboard/overv…

什么是P2P?

P2P (Peer-to-Peer) 是一种分布式的网络架构&#xff0c;其中各个节点&#xff08;通常被称为“peers”或“节点”&#xff09;直接进行数据共享和交换&#xff0c;而无需依赖中央服务器。P2P 网络强调平等的参与和共享&#xff0c;每个节点既可以是数据的消费者&#xff08;下…

从MySQL到金蝶云星空通过接口配置打通数据

从MySQL到金蝶云星空通过接口配置打通数据 对接系统&#xff1a;MySQL MySQL是一个关系型数据库管理系统&#xff0c;由瑞典MySQLAB公司开发&#xff0c;属于Oracle旗下产品。MySQL是最流行的关系型数据库管理系统之一&#xff0c;在WEB应用方面&#xff0c;MySQL是最好的RDBMS…