再探Java集合系列—ArrayList

适用于什么场景?

检索比较多的场景,例如学生成绩管理系统,老师对学生的成绩进行排名或查询操作

ArrayList有哪些特点?

1、ArrayList集合底层采用了数组数据结构,是Object类型

2、动态数组。ArrayList的默认初始容量为10,扩容因子为1.5,数组长度随着容量的增长数组长度。但是数组的长度并不会随着ArrayList的容量立即缩小,除非显示的调用 trimToSize 方法

3、建议给定一个预估计的初始化容量,减少数组扩容的次数,这是ArrayList集合比较重要的优化策略.因为在在扩容的同时需要将原来数组中的数据复制到新数组里,但如果要插入大量数据时,赋值数组的形式效率很低,所以大多数情况下会使用带参构造函数,传入一个预估计容量,提前定义好容量。

4、ArrayList是非线程安全

单独看这些特点我们还是回觉得有些枯燥,结合具体场景我们来分析分析

实战演练

import java.util.ArrayList;
import java.util.List;

public class ListTest {
public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    list.add("b");//第一个,索引下标0
    list.add("d");
    list.add("c");
    list.add("a");
    list.add("d"); //允许使用重复元素
    
    System.out.println(list);  //输出结果:[b, d, c, a, d]
    
    System.out.println(list.get(2));  //输出指定下标的元素,输出结果:c
    
    list.add(1,"f");//在指定索引下标位置添加元素
    System.out.println(list); //输出结果:[b, f, d, c, a, d],原来下标为1和1之后的下标索引位置的元素自动向后移动
    
    List<String> a = new ArrayList<String>();
    a.add("123");
    a.add("456");
    list.addAll(2,a);  //在指定索引下标的位置插入集合
    System.out.println(list);//输出结果:[b, f, 123, 456, d, c, a, d]
    
    //获取指定元素在集合中第一次出现的索引下标
    System.out.println(list.indexOf("d")); //输出结果:4
    
    //获取指定元素在集合中最后一次出现的索引下标
    System.out.println(list.lastIndexOf("d"));//输出结果:7
    
    list.remove(2);  //根据指定的索引下标移除元素
    System.out.println(list);  //输出结果:[b, f, 456, d, c, a, d]
    
    list.set(1,"ff"); //根据指定的索引下标修改元素
    System.out.println(list); //输出结果:[b, ff, 456, d, c, a, d]
    
    //根据索引下标的起始位置截取一段元素形成一个新的集合,截取的时候,包含开始的索引不包含结束时的索引
    List<String> sublist= list.subList(2,4);
    System.out.println(sublist);//输出结果:[456, d]
    
    System.out.println(list.size());//输出结果7
    }
}
import java.util.LinkedList;
import java.util.List;

public class ListTest {
public static void main(String[] args){
    List l1 = new LinkedList();
    
    for(int i = 0;i<=5;i++){
    	l1.add("a"+i);
    }
    
    System.out.print(l1);
    
    l1.add(3,"a100");
    System.out.println(l1);
    
    l1.set(6,"a200");
    System.out.println(l1);
    
    System.out.print((String)l1.get(2)+" ");
    
    System.out.println(l1.indexOf("a3"));
    
    l1.remove(1);
    System.out.println(l1);
    }
}

输出结果:

[a0,a1,a2,a3,a4,a5]

[a0,a1,a2,a100,a3,a4,a5]

[a0,a1,a2,a100,a3,a4,a200]

a2 4

[a0,a2,a100,a3,a4,a200]

底层原理

有几个变量在之后增删改查方法中会反复使用,我们需要注意

注意:
  • 数组长度是指当前数组内元素的个数
  • 数组容量是指数组所能容纳的长度

①、序列化和反序列化问题

在方法签名上我们看到ArrayList类实现了Serializable接口,说明我们创建的ArrayList数组可以序列化(存储数据库、传输数据等)和反序列化,但是用于存储元素的数组elementData为什么还用transient关键字修饰呢?我们都知道用transient关键字修饰的变量可以不进行序列化和反序列化,那这样做是为什么呢?

大家设想一个场景:此时我的数组长度为15,但实际元素大小为11,是不是剩余4个空间没有用到?如果我们在序列化和反序列化的时候是不是就要多序列化和反序列化4个空间的内容,是不是浪费了无效的操作?所以秉持着高效第一的原则减少无效操作。在ArrayList的底层有两个方法readObject和writeObject用于序列化

此时我们会发现在遍历的范围是0到实际数组的大小,拿上面的场景来说就是0-10的范围,序列化数组中0-10的元素,这样没有用到的4个空间是不是就没有被序列化和反序列化。

②、添加元素——add()

思想:

创建一个Object类型的空数组(注意:当第一次add添加元素的时候,才指定默认容量为10)

ensureCapacityInternal方法先判断容量值是否大于当前ArrayList的容量,如果大于当前集合容量,则需要调用grow方法进行扩容;反之,不用操作

③、grow扩容——ArrayList扩容机制

ArrayList的使用前不需要像数组一样提前定义大小空间,容量是随着使用时自动增长的,那为什么在使用ArrayList的add方法添加元素的时候底层还需要判断集合的容量是否能够放下要添加的元素呢?又没有定义固定大小直接放进去不就好了吗?

add方法添加分为三步:

①、判断集合容量是否满足添加的元素

②、添加元素

③、集合长度+1

什么时候需要扩容?

如果当前容量+ 1超过数组长度

用户不需要提前定义大小,那是因为底层默认已经定义好了大小。其实是有一个边界值的,并不是无限增长的。使用时增加,是因为底层有扩展因子(扩容因子是1.5),当数量达到数组的百分之多少的时候就会扩容。ArrayList默认的初始大小是10,其实在一开始new完之后的数组容量并不是10,而且一个空的数组,当添加第一个元素的时候会进行第一次扩容,数组容量变为10

ArrayList扩容的时候会将原来的数组复制到一个新的数组中,为什么这么做?那原来的数组什么时候回收?

当 ArrayList 需要扩容时,会创建一个新的更大的数组,并将原来的数组中的元素复制到新数组中。这样做的原因是为了确保数组的连续性,以便能够快速地访问和修改元素。如果不进行数组复制,而是直接在原数组上进行扩容,可能会因为内存不连续而导致性能下降

原来的数组会在扩容后变得多余,不再被使用。原来的数组会在没有任何引用指向它时变为不可达,即没有任何变量指向原数组时,原数组会成为垃圾对象。一旦原数组成为不可达的垃圾对象,垃圾回收器就会在适当的时候将其回收,释放其占用的内存空间。这个过程是由垃圾回收器自动管理的,程序员不需要显式地释放原数组。

④、在指定位置插入新元素——add()

当我们在指定位置插入元素的时候,要插入下标的后面元素会整体向后移动一位,增加了系统额外的系统开销,如上面的图片例子来说:如果要插入位置越靠近数组前面,我们会发现数组的移动变得很大

⑤、更新元素——set()

⑥、删除元素——remove()

不管是删除指定位置元素和直接删除元素都涉及到了数组元素的移动,所以我们要删除的元素如果越靠近数组的前面,所消耗的性能越大

注意:不要遍历集合删除元素,会出现数据不一致问题,个别元素没有删除成功

⑦、查找元素——indexOf()

因为数组有一个特点是可以根据下标查找元素,如果按照指定下标查找元素,ArrayList的性能会很高,但是根据上图的源码我们不难发现:如果是根据元素查找下标,会从头到尾遍历整个数组,如果数组的位置特别靠近末尾,那整个查询会非常耗时


出现的问题:

线程安全问题:当多线程环境下同时对集合操作(添加、删除、修改元素),可能导致数据不一致问题(数组越界、数据丢失等)

解决方案:

  1. 使用CopyOnWriteArrayList线程安全集合
  2. 使⽤ Collections.synchronizedList 包装 ArrayList,然后操作包装后的 list


CopyOnWriteArrayList

CopyOnWrite — —写时复制

读操作是⽆锁的,性能较⾼;写操作的时候先将当前容器复制一份,然后在新数组上执行写操作,结束之后再将原容器的引用指向新容器

参考网上图片

备:参考网上图片

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

西南科技大学(数据结构A)期末自测练习二

一、填空题(每空1分,共10分) 1、在线性表的下列运算中,不改变数据元素之间结构关系的运算是( D ) A、插入 B、删除 C、排序 D、定位 2、顺序表中第一个元素的存储地址是100,每个元素的长度为2,则第5个元素的地址是( B ) A.110 B.108 C.100 …

爱普生L3153变ET-2710修复

晚上还在加班&#xff0c;老婆发来消息说打印机故障了&#xff0c;通过网络不能访问 回家一下&#xff0c;三个灯&#xff08;电源&#xff0c;网络&#xff0c;墨水&#xff09;闪烁 重启多次没效果&#xff0c;问客服&#xff0c;说是存储错误&#xff0c;要送售后&#xff…

4.4-Docker bridge0详解

在Docker世界中&#xff0c;两个container是通过bridge0连接起来的。 首先&#xff0c;介绍一个命令&#xff1a;docker network ls 这个docker network ls明令会列举出来当前这台机器上docker有哪些网络。 先看一下bridge。 现在有一个容器flask-hello-docker&#xff0c;它是…

接手了一个外包开发的项目,我感觉我的头快要裂开了~

嗨&#xff0c;大家好&#xff0c;我是飘渺。 最近&#xff0c;我和小伙伴一起接手了一个由外包团队开发的微服务项目&#xff0c;这个项目采用了当前流行的Spring Cloud Alibaba微服务架构&#xff0c;并且是基于一个“大名鼎鼎”的微服务开源脚手架&#xff08;附带着模块代…

IDEA编译器的永久试用设置与基本使用

参考视频&#xff1a; 最通俗易懂的JDK、IDEA的安装使用权威指南 2023新版前端Web开发HTML5CSS3移动web视频教程&#xff0c;前端web入门首选黑马程序员 文章目录 一.安装包下载与安装二.设置IDEA永久试用三.IDEA的基本试用0.IDEA管理Java程序的结构1.工程创建2.模块创建3.包创…

Anolis 安装 Conda 和 YoloV8

Anolis 安装 Conda 和 YoloV8 一 Conda 和 YoloV8 安装1.Conda 下载与安装2.YoloV8 安装 二.测试 一 Conda 和 YoloV8 安装 ## 1. anolis 安装 cv2 依赖库 yum install -y mesa-libGL.x86_64 ## Anaconda https://repo.anaconda.com/archive/ ## 重启终端查看版本 conda --ver…

Linux处理文件常见命令

目录 1 cp 2 rm 3 zip与unzip 3.1 zip 3.2 unzip 4 cd 5 ls 6 chmod 7 scp 7.1 文件在你操作的机器上&#xff0c;你要传给另一个机器 7.1.1 文件 7.1.2 文件夹 7.2 文件在另一个机器上&#xff0c;你要把文件搞到你操作的机器上 7.2.1 文件 7.2.…

上海亚商投顾:沪指震荡反弹 汽车产业链掀涨停潮

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 三大指数昨日震荡反弹&#xff0c;北证50指数跌超4%&#xff0c;近50只北交所个股跌超10%。 新能源车产业链掀…

leetCode 216.组合总和 III + 回溯算法 + 剪枝 + 图解 + 笔记

找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回 示例 1: 输入: k 3, n 7 输出: [[1,2,4]] 解释…

Istio新架构揭秘:环境化Mesh

自问世以来&#xff0c;Istio因其使用Sidecar&#xff08;可编程代理与应用容器一同部署&#xff09;而备受认可。这种架构选择使Istio用户能够享受其好处&#xff0c;而无需对其应用进行 drast 改变。这些可编程代理&#xff0c;与应用容器紧密部署在一起&#xff0c;因其能够…

Langchain-Chatchat学习

参考&#xff1a;Langchain-Chatchat 阿里通义千问Qwen 保姆级教程 | 次世代知识管理解决方案 - 知乎 (zhihu.com) 中文LLM生态观察 模型 就开源的部分而言&#xff0c;从一开始的MOSS[1] ChatGLM[2] ChatGLM2 [3] 到后来的 baichan [4] 基于LLama2 微调的 中文LLama2 [5] …

Blender学习笔记:小车狂奔动画

文章目录 路旁小树汽车尾气移动 教程地址&#xff1a;八个案例教程带你从0到1入门blender【已完结】 小车建模 路旁小树 1 添加摄像机&#xff0c;在小车下面拉一个平面&#xff0c;覆盖到摄像机的观察视窗。复制一层平面&#xff0c;收窄变成小车两侧的路面&#xff0c;编辑…

项目:基于UDP的网络聊天室

项目需求&#xff1a; 1.如果有用户登录&#xff0c;其他用户可以收到这个人的登录信息 2.如果有人发送信息&#xff0c;其他用户可以收到这个人的群聊信息 3.如果有人下线&#xff0c;其他用户可以收到这个人的下线信息 4.服务器可以发送系统信息 服务器代码&#xff1a; #i…

环境监测传感器守护我们的地球

随着人类活动的不断增加&#xff0c;环境问题日益凸显。为了更好地保护我们的地球&#xff0c;环境监测成为了一项非常重要的任务。而在这个领域&#xff0c;传感器技术发挥着至关重要的作用。今天&#xff0c;我们就来聊聊WX-WQX12 环境监测传感器。 环境监测传感器是一种能够…

IDEA 配置 gradle6.8.3 解决导入gradle项目下载太慢问题

由于平时用的是springboot 2.7 这里下载gradle-6.8.3 Gradle官网地址&#xff1a;https://services.gradle.org/distributions/ 1.下载gradle后&#xff0c;配置环境变量 GRADLE_HOME {gradle 文件路径} GRADLE_USER_HOME {jar下载路径&#xff0c;可以放maven jar保存路径…

浅谈安科瑞网络电力仪表在斯里兰卡某项目的应用

摘要&#xff1a;安科瑞APM系列网络仪表适用于高低压柜&#xff0c;进线以及出线处的全电量测量及监测。 Absrtact: APM series of network power meter are suitable for full power measurement and monitoring of high and low voltage cabinets, incoming and outgoing li…

Java小游戏 王者

GameFrame类 所需图片&#xff1a; package 王者荣耀;import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.File; import java.util.ArrayList…

【论文复现】RoSteALS: Robust Steganography using Autoencoder Latent Space-2023-CVPR

一定要按照dockerfile&#xff0c;requirements.txt和requirements2.txt配置环境 需要补充的库&#xff1a; pip安装&#xff1a;omegaconf slack slackclient bchlib (0.14.0版本) einops imagenet-c conda安装&#xff1a;scikit-image&#xff0c;matplotlib 按照作者git的…

浅谈安科瑞电力监控系统在百事亚洲研发中心的应用

摘要&#xff1a;介绍百事亚洲研发中心&#xff0c;采用智能电力仪表、采集配电现场的各种电参量和开关信号。系统采用现场就地组网的方式&#xff0c;组网后通过现场总线通讯并远传至后台&#xff0c;通过Acrel-2000型电力监控系统实现配电所配电回路用电的实时监控和管理。 …

再探Java集合系列—LinkedList

单向链表 双向链表 LinkedList适用于什么场景&#xff1f; 适用于需要频繁插入和删除元素的场景&#xff0c;例如消息聊天系统&#xff0c;一开始并不明确有多少记录&#xff0c;可以在空间满足的情况下不断增加数据 LinkedList的特点有哪些&#xff1f; LinkedList的底层采…