讲师中心 微信公众号
AI工具推荐 视频效率加速

如何在Java中使用ArrayList Java ArrayList增删查改操作

浅瑶同学_5482

浅瑶同学_5482

发布时间:2025-07-18 17:34:01

|

1172人浏览过

|

来源于php中文网

原创

java中的arraylist是基于动态数组实现的集合,支持灵活的增删查改操作。1. 添加元素时,使用add()方法可在末尾或指定索引插入,addall()可批量添加;2. 查询通过get()按索引获取元素,size()获取大小,contains()判断是否包含某元素,isempty()判断是否为空;3. 修改使用set()替换指定位置元素,返回旧值;4. 删除可通过remove(index)按索引或remove(object)按对象删除,clear()清空列表。arraylist相比传统数组更灵活,自动扩容机制使其适用于数据量不确定的场景,但频繁扩容可能影响性能。并发环境下,arraylist非线程安全,可使用collections.synchronizedlist或copyonwritearraylist等替代方案。

如何在Java中使用ArrayList Java ArrayList增删查改操作

Java中的ArrayList,说白了,就是个动态数组。它能让你在编程时,不用像传统数组那样,一开始就得把大小定死。你可以随时往里面加元素,删元素,改元素,甚至查元素,它自己会根据需要自动扩容或缩小。在我看来,这玩意儿是Java集合框架里最常用,也最基础的一个工具,几乎所有需要列表数据结构的地方,你都能看到它的身影。理解它的增删查改,基本上就掌握了Java日常开发中数据操作的一大半。

如何在Java中使用ArrayList Java ArrayList增删查改操作

解决方案

使用ArrayList进行增删查改操作,其实非常直观,API设计得相当人性化。

1. 初始化与添加元素(Add)

立即学习“Java免费学习笔记(深入)”;

如何在Java中使用ArrayList Java ArrayList增删查改操作

创建一个ArrayList很简单,你需要指定它存放的元素类型,这得益于Java的泛型。

import java.util.ArrayList;
import java.util.List; // 通常我们用接口类型声明变量

public class ArrayListDemo {
    public static void main(String[] args) {
        // 创建一个存放字符串的ArrayList
        List<String> names = new ArrayList<>(); 

        // 添加元素到列表末尾
        names.add("张三");
        names.add("李四");
        names.add("王五");
        System.out.println("添加后的列表: " + names); // 输出: [张三, 李四, 王五]

        // 在指定位置插入元素(索引从0开始)
        // 注意:如果索引超出当前列表大小,会抛出IndexOutOfBoundsException
        names.add(1, "赵六"); 
        System.out.println("插入后的列表: " + names); // 输出: [张三, 赵六, 李四, 王五]

        // 也可以一次性添加另一个集合的所有元素
        List<String> moreNames = new ArrayList<>();
        moreNames.add("钱七");
        moreNames.add("孙八");
        names.addAll(moreNames);
        System.out.println("批量添加后的列表: " + names); // 输出: [张三, 赵六, 李四, 王五, 钱七, 孙八]
    }
}

这里要注意,add(index, element) 这个方法,如果你给的 index 大于 size(),或者小于0,那就会直接抛出 IndexOutOfBoundsException。这不像数组,你不能直接跳过中间的索引去赋值。

如何在Java中使用ArrayList Java ArrayList增删查改操作

2. 访问/查询元素(Get/Query)

获取ArrayList中的元素,主要是通过索引来完成。

public class ArrayListDemo {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("张三");
        names.add("李四");
        names.add("王五");

        // 获取指定索引的元素
        String firstPerson = names.get(0);
        System.out.println("第一个人是: " + firstPerson); // 输出: 第一个人是: 张三

        // 获取列表的大小(元素数量)
        int size = names.size();
        System.out.println("列表大小: " + size); // 输出: 列表大小: 3

        // 判断列表是否为空
        boolean isEmpty = names.isEmpty();
        System.out.println("列表是否为空: " + isEmpty); // 输出: 列表是否为空: false

        // 判断列表是否包含某个元素
        boolean containsLiSi = names.contains("李四");
        System.out.println("是否包含李四: " + containsLiSi); // 输出: 是否包含李四: true

        // 遍历列表
        System.out.println("遍历列表:");
        for (String name : names) {
            System.out.println("- " + name);
        }
        // 或者使用传统的for循环
        // for (int i = 0; i < names.size(); i++) {
        //     System.out.println("- " + names.get(i));
        // }
    }
}

get(index) 同样需要注意索引的边界,超出范围也会抛出 IndexOutOfBoundsException。

3. 修改元素(Set/Update)

如果你想替换ArrayList中某个位置的元素,而不是插入或删除,set() 方法就派上用场了。

public class ArrayListDemo {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("张三");
        names.add("李四");
        names.add("王五");
        System.out.println("原始列表: " + names); // 输出: [张三, 李四, 王五]

        // 修改索引1的元素,将"李四"改为"小明"
        // set方法会返回被替换掉的旧元素
        String oldName = names.set(1, "小明"); 
        System.out.println("修改后的列表: " + names); // 输出: [张三, 小明, 王五]
        System.out.println("被替换掉的是: " + oldName); // 输出: 被替换掉的是: 李四
    }
}

set(index, element) 同样需要确保 index 在合法范围内 [0, size()-1],否则会抛出 IndexOutOfBoundsException。

4. 删除元素(Remove)

删除操作有两种主要方式:按索引删除和按对象删除。

public class ArrayListDemo {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("张三");
        names.add("李四");
        names.add("王五");
        names.add("李四"); // 再次添加李四,看看按对象删除的效果
        System.out.println("原始列表: " + names); // 输出: [张三, 李四, 王五, 李四]

        // 按索引删除元素
        // 删除索引为2的元素(王五)
        String removedByIndex = names.remove(2); 
        System.out.println("按索引删除后的列表: " + names); // 输出: [张三, 李四, 李四]
        System.out.println("按索引删除的是: " + removedByIndex); // 输出: 按索引删除的是: 王五

        // 按对象删除元素
        // 注意:如果列表中有多个相同的对象,只会删除第一个匹配到的
        boolean removedByObject = names.remove("李四"); 
        System.out.println("按对象删除后的列表: " + names); // 输出: [张三, 李四]
        System.out.println("是否成功按对象删除: " + removedByObject); // 输出: 是否成功按对象删除: true

        // 清空所有元素
        names.clear();
        System.out.println("清空后的列表: " + names); // 输出: []
        System.out.println("清空后列表是否为空: " + names.isEmpty()); // 输出: 清空后列表是否为空: true
    }
}

remove(index) 也会检查索引合法性。而 remove(Object) 则依赖于对象自身的 equals() 方法来判断是否匹配。如果列表包含 null,你也可以 remove(null) 来删除第一个 null。

ArrayList与传统数组:选择的智慧

很多初学者可能会纠结,既然Java有传统的数组(String[],int[]),那为什么还需要ArrayList这种东西呢?它们之间最大的区别,也是我们选择时最核心的考量,就在于“灵活性”和“性能”。

AI舌诊面诊问诊医疗系统+诊疗商城系统源码
AI舌诊面诊问诊医疗系统+诊疗商城系统源码

AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。

下载

传统数组一旦创建,大小就是固定的,比如你 new String[10],那它就只能装10个字符串,不多不少。如果你想加第11个,或者删掉一个让数组变小,那就麻烦了,你得新建一个更大或更小的数组,然后把旧数组里的元素一个一个地复制过去。这在数据量不确定或者需要频繁变动的情况下,简直是噩梦。

而ArrayList呢,它骨子里也是基于数组实现的,但它把数组的扩容、缩容这些繁琐的事情都封装起来了。当你的ArrayList快满了,它会自动帮你创建一个更大的内部数组,然后把旧数据复制过去。这就像一个自带“弹性”的容器,你不用操心它的容量问题。

所以,何时选择?

  • 如果你明确知道数据的数量,并且这个数量在程序运行过程中不会改变,或者改变非常少,那么传统数组可能更合适。 因为它在内存上更紧凑,访问元素(通过索引)的速度理论上更快一点点,毕竟少了一层封装的开销。
  • 但如果你的数据量不确定,或者需要频繁地添加、删除元素,那么ArrayList几乎是你的不二之选。 它的动态性大大简化了代码,提高了开发效率。在绝大多数业务场景下,ArrayList那点因为自动扩容带来的性能开销,相比它带来的便利性,几乎可以忽略不计。当然,如果是在对性能极其敏感,比如百万级甚至千万级数据量的循环操作中,我们可能就需要更深入地考虑数据结构,但那属于另一个层面的优化了。

深入探究:ArrayList的内部扩容机制与性能考量

我们说ArrayList是“动态数组”,那这个“动态”到底是怎么实现的?这背后其实藏着一个巧妙的扩容机制,理解它,能帮助我们更好地优化代码,避免一些潜在的性能陷阱。

ArrayList内部维护着一个普通的Java数组(Object[] elementData),这就是它真正存储元素的地方。当你初始化一个ArrayList但没有指定容量时,它通常会有一个默认的初始容量(比如JDK 8中是10)。

当你在不断地往ArrayList里 add() 元素,直到内部数组装不下的时候,ArrayList就会触发一次“扩容”操作。这个过程大致是这样的:

  1. 计算新容量: ArrayList会计算一个新的容量,通常是当前容量的1.5倍(oldCapacity + (oldCapacity >> 1)),或者在某些情况下,如果这个新容量还是不够,就直接取你需要的最小容量。
  2. 创建新数组: 根据计算出来的新容量,创建一个全新的 Object[] 数组。
  3. 数据复制: 使用 System.arraycopy() 方法,将旧数组中的所有元素高效地复制到新数组中。
  4. 替换引用: 将内部的 elementData 引用指向这个新的大数组,旧的数组就等待垃圾回收了。

这个扩容过程对性能的影响主要体现在“数据复制”上。System.arraycopy() 是一个native方法,效率很高,但毕竟涉及到内存的搬迁。如果你的ArrayList频繁地触发扩容,比如你每次只添加一个元素,然后就满了,又扩容,又满了……那么每次扩容都会带来一次O(N)的时间复杂度开销(N是当前元素数量)。这会让一系列看似O(1)的 add() 操作,在某些时刻突然变成O(N),导致性能出现“尖峰”。

如何优化? 如果你预估ArrayList将要存储大量元素,或者大致知道元素的最大数量,可以在创建ArrayList时就指定一个合适的初始容量:

// 预估将有1000个元素
List<String> largeList = new ArrayList<>(1000); 

这样做可以有效减少扩容的次数,从而避免多次数据复制的开销,提升整体性能。当然,如果你预估的容量过大,而实际只用了很少一部分,那也会造成内存的浪费。所以,这是一个权衡的问题,需要根据实际场景来把握。

另一个方法是,当你知道不会再添加元素,但当前容量远大于实际元素数量时,可以调用 trimToSize() 方法来裁剪内部数组,使其容量与当前元素数量一致,从而释放多余的内存空间。但这通常在列表生命周期即将结束,或者内存特别吃紧时才考虑。

并发考量:ArrayList的线程安全性与替代方案

在多线程环境下使用ArrayList,是个需要特别小心的地方。简单来说,ArrayList本身不是线程安全的。这意味着,如果你有多个线程同时对同一个ArrayList进行修改(比如一个线程在添加,另一个线程在删除),或者一个线程在修改而另一个线程在遍历,很可能会出现数据不一致、丢失数据,甚至抛出 ConcurrentModificationException 这样的运行时异常。

为什么会这样?因为ArrayList的内部操作,比如 add()、remove() 等,都没有进行同步控制。当一个线程正在执行扩容,而另一个线程同时尝试访问或修改,就可能读到不完整的数据,或者破坏了内部数组的结构。ConcurrentModificationException 尤其常见于迭代时修改,因为迭代器通常会检查列表的“修改次数”,一旦发现不一致就会报错。

那么,在并发场景下,我们该如何安全地使用类似ArrayList的功能呢?

  1. 使用 Collections.synchronizedList() 包装: 这是最简单粗暴的方法,它会返回一个线程安全的List。

    List<String> safeList = Collections.synchronizedList(new ArrayList<>());

    这个方法给ArrayList的所有公共方法都加上了 synchronized 关键字。但需要注意的是,迭代器仍然不是线程安全的。如果你在迭代 safeList 的同时,有其他线程修改了它,依然可能抛出 ConcurrentModificationException。所以,在迭代时,通常需要手动加锁:

    synchronized (safeList) {
        for (String item : safeList) {
            System.out.println(item);
        }
    }

    这种方式的缺点是,同步粒度较大,所有操作都串行化,在高并发下性能可能会受影响。

  2. 使用 java.util.concurrent 包下的并发集合:CopyOnWriteArrayList 这是Java并发包提供的一个专门为并发场景设计的ArrayList替代品。它的特点是:

    • 读写分离: 在进行修改操作(add, set, remove)时,CopyOnWriteArrayList 会创建一个底层数组的副本,在新副本上进行修改,修改完成后再把引用指向新副本。
    • 读操作无锁: 由于写操作是在副本上进行的,读操作可以直接读取旧的(未修改的)数组,因此读操作是完全无锁的,性能很高。
    • 适用于读多写少: 这种机制非常适合读操作远多于写操作的场景。
    • 缺点: 每次写操作都会复制整个数组,如果列表非常大,或者写操作非常频繁,会带来显著的内存开销和性能损耗。同时,读到的数据可能是“旧”的,即写操作完成前的数据。
    import java.util.concurrent.CopyOnWriteArrayList;
    
    CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<>();
    cowList.add("A");
    cowList.add("B");
    // 在多线程环境下,读操作很安全,写操作会有副本开销
  3. 使用 Vector(不推荐):Vector 是ArrayList的“老前辈”,它从一开始就是线程安全的,所有方法都用 synchronized 修饰。但由于其同步粒度大,性能通常不如ArrayList,而且在现代Java开发中,有了更灵活、性能更好的并发工具,Vector 已经很少使用了。

选择哪种方案,取决于你的具体需求:是读多写少,还是写多读少?对实时性要求如何?通常来说,如果只是简单的线程安全需求,Collections.synchronizedList 可以应付;如果读操作非常频繁,CopyOnWriteArrayList 是个不错的选择;而更复杂的并发场景,则可能需要更细粒度的锁控制或者其他并发集合(如 ConcurrentHashMap 等)。

热门AI工具

更多
创客贴AI

创客贴AI是一款AI图像与设计工具,AI辅助的智能在线设计工具。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

6759

2023.08.02

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

549

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1698

2024.03.01

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

1798

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

2844

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

6184

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

5969

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

7574

2024.03.22

Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

20

2026.10.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Gradle 安全最佳实践
Gradle 安全最佳实践

共0课时 | 0人学习

Gradle Wrapper 使用与安全指南
Gradle Wrapper 使用与安全指南

共0课时 | 0人学习

Golangci-lint命令行使用演示
Golangci-lint命令行使用演示

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn