开发场景中Java 集合的最佳选择

news2025/1/2 22:57:33

在 Java 开发中,集合类是处理数据的核心工具。合理选择集合,不仅可以提高代码效率,还能让代码更简洁。本篇文章将重点探讨 List、SetMap 的适用场景及优缺点,帮助你在实际开发中找到最佳解决方案。

一、List:有序存储的/最佳选择

1. ArrayList:快速查询与动态数组

应用场景:当你需要频繁查询元素,或者存储的元素数目动态变化时,ArrayList 是首选。例如:分页展示用户数据。

代码示例:

List<String> users = new ArrayList<>();
users.add("Alice");
users.add("Bob");
System.out.println(users.get(1)); // 输出 Bob

底层结构ArrayList 使用一个 动态数组 来存储元素。初始时,数组的大小是固定的,当元素超过数组的容量时,会自动扩展数组的大小。

优点

  • 查询效率高:数组支持按索引快速访问元素,时间复杂度为 O(1),因此 get() 操作非常高效。

  • 内存局部性:数组存储在连续的内存空间中,CPU 缓存友好,可以利用 CPU 的缓存机制提高访问效率。

缺点

  • 插入和删除效率低:当插入或删除元素时,尤其是在中间位置时,必须移动数组中的大量元素,时间复杂度为 O(n)

  • 扩容操作代价高:数组扩容时需要分配新的数组并将旧数组元素复制到新数组,操作的时间复杂度为 O(n)


2. LinkedList:高效增删的双向链表

应用场景:需要频繁在列表中间或首尾插入、删除数据时,例如实现任务队列。

代码示例:

LinkedList<String> tasks = new LinkedList<>();
tasks.addFirst("Task1");
tasks.addLast("Task2");
tasks.removeFirst();

底层结构LinkedList 使用 双向链表,每个元素都有两个指针:一个指向前一个元素,一个指向下一个元素。这样可以在常数时间内插入或删除元素。

优点

  • 插入和删除高效:无论是在链表的头部、中部还是尾部,插入和删除元素的时间复杂度都为 O(1),因为只需要改变相关节点的指针。

  • 内存使用灵活:每个元素的内存可以分散存储,不需要连续的内存块。

【比如在中间插入

假设需要在链表中的某个位置 node 前插入新节点 newNode

  1. newNodenext 指向 node,将 newNodeprev 指向 node.prev

  2. 更新 node.prev.nextnewNode,更新 node.prevnewNode

这只涉及 4 次指针操作,与链表的长度无关,因此在已定位到目标节点后,插入操作的时间复杂度为 O(1)

缺点

  • 查询效率低:为了查找元素,必须从头节点或尾节点开始遍历链表,时间复杂度为 O(n)

  • 内存开销大:每个元素都需要额外存储指向前后元素的指针,相较于数组,占用更多的内存。


二、Set:无重复集合的首选

1. HashSet:高效去重

应用场景:当需要存储一组不允许重复的元素,且对顺序没有要求时,例如用户注册时验证用户名的唯一性。

代码示例

Set<String> usernames = new HashSet<>();
usernames.add("Alice");
usernames.add("Bob");
usernames.add("Alice"); // 重复的元素会被忽略
System.out.println(usernames.size()); // 输出 2

底层结构HashSet 使用 哈希表HashMap)【哈希表在文末有补充讲解】来存储元素。哈希表通过将元素的哈希码映射到表中的桶来进行存储,确保元素是唯一的。

优点

  • 去重高效:哈希表能够快速判断元素是否已存在,因为它通过哈希值进行查找,时间复杂度为 O(1)

  • 查询效率高:哈希表的查找时间复杂度为 O(1),因此 contains()add() 操作非常高效。

缺点

  • 无序存储:哈希表并不维护元素的顺序,因此 HashSet 中的元素是无序的。

  • 哈希冲突:不同的元素可能具有相同的哈希值,哈希冲突会影响性能,但通常情况下,哈希表的设计会尽量减少冲突的概率。


2. LinkedHashSet:有序去重

应用场景:当需要去重的同时保留插入顺序,例如记录用户最近浏览的商品。

代码示例:

Set<String> products = new LinkedHashSet<>();
products.add("Laptop");
products.add("Phone");
products.add("Laptop"); // 再次添加无效
System.out.println(products); // 输出 [Laptop, Phone]

底层结构LinkedHashSet 使用一个 哈希表 来存储元素,并通过一个 双向链表 来维护元素的插入顺序。

优点

  • 有序存储:由于链表的存在,LinkedHashSet 能够保持元素的插入顺序,访问时能够按照插入的顺序遍历元素。

  • 去重高效:与 HashSet 一样,哈希表提供了快速的查找和去重机制。

缺点性能略低于 HashSet,由于还需要维护链表,LinkedHashSet 的操作稍微比 HashSet 慢,但差距通常不大。


3. TreeSet:排序与去重兼备

应用场景:当需要去重的同时对元素进行排序,例如实现排行榜或数据字典。

代码示例:

TreeSet<Integer> scores = new TreeSet<>();
scores.add(50);
scores.add(80);
scores.add(70);
System.out.println(scores); // 输出 [50, 70, 80]

底层结构TreeSet 使用 红黑树 来存储元素。红黑树是一种自平衡的二叉搜索树,能够确保树的深度保持在对数级别。

优点

  • 有序存储TreeSet 会自动对元素进行排序,默认按自然顺序排序compareTo(Object obj))或者通过传入 Comparator 自定义排序)。

  • 查找、插入和删除的时间复杂度为 O(log n):由于红黑树的结构特性,所有操作的时间复杂度为对数级别。

缺点性能较低,相比哈希表,红黑树的插入、删除和查找操作的时间复杂度为 O(log n),因此在大量数据操作时,性能略逊色于 HashSetLinkedHashSet

三、Map:键值对存储的首选

Map 是存储键值对的集合类,每个键唯一对应一个值。常用于快速查找和关联关系的存储。

1. HashMap:高效的键值映射

应用场景:需要高效查找时,例如存储用户 ID 和用户信息的映射。

代码示例:

Map<Integer, String> userMap = new HashMap<>();
userMap.put(1, "Alice");
userMap.put(2, "Bob");
System.out.println(userMap.get(1)); // 输出 Alice

底层结构HashMap 使用 哈希表 来存储键值对,通过键的哈希码来确定存储位置。

优点

  • 查找和插入高效:查找、插入和删除操作的时间复杂度为 O(1),通过哈希值直接定位位置。

  • 支持键值对的存储:每个键对应唯一的值,适合各种映射操作。

缺点

  • 无序存储:哈希表中的元素是无序的,因此遍历时无法保证顺序。


2. LinkedHashMap:有序的键值映射

应用场景:需要既保持插入顺序,又能高效查找,例如实现最近访问页面的缓存。

代码示例:

Map<Integer, String> accessLog = new LinkedHashMap<>();
accessLog.put(1, "HomePage");
accessLog.put(2, "ProfilePage");
accessLog.put(3, "SettingsPage");
System.out.println(accessLog); // 输出 {1=HomePage, 2=ProfilePage, 3=SettingsPage}

底层结构LinkedHashMap 使用 哈希表 存储元素,并通过 双向链表 维护元素的插入顺序。

优点

  • 有序存储:保持了元素的插入顺序,遍历时能够按照插入顺序输出。

  • 高效查找:与 HashMap 一样,查询和插入操作的时间复杂度为 O(1)

缺点内存开销较大,需要额外的内存来存储链表指针。


3. TreeMap:有序的键值存储

应用场景:需要按键排序存储键值对,例如实现字典或排行榜。

代码示例:

Map<Integer, String> sortedMap = new TreeMap<>();
sortedMap.put(3, "C");
sortedMap.put(1, "A");
sortedMap.put(2, "B");
System.out.println(sortedMap); // 输出 {1=A, 2=B, 3=C}
  • 底层结构TreeMap 使用 红黑树 来存储键值对,按照键的自然顺序(或通过指定的 Comparator)进行排序。

  • 优点

    • 有序存储:自动对键进行排序,适用于需要顺序访问键值对的场景。

    • 高效的查找、插入和删除:操作时间复杂度为 O(log n)

  • 缺点性能略低于 HashMapLinkedHashMap,由于红黑树需要维护平衡,操作的时间复杂度为对数级别,性能不如哈希表

4. Properties

应用场景

  • Properties 常用于管理应用程序的配置信息,如数据库连接信息、语言国际化资源等。

  • 它可以方便地加载和存储键值对到 .properties 文件中,支持流式操作。

代码示例:

import java.io.*;
import java.util.Properties;
​
public class PropertiesExample {
    public static void main(String[] args) throws IOException {
        Properties properties = new Properties();
        
        // 设置键值对
        properties.setProperty("database.url", "jdbc:mysql://localhost:3306/mydb");
        properties.setProperty("database.user", "root");
        properties.setProperty("database.password", "password");
​
        // 保存到文件
        try (FileOutputStream output = new FileOutputStream("config.properties")) {
            properties.store(output, "Database Configuration");
        }
​
        // 从文件加载
        try (FileInputStream input = new FileInputStream("config.properties")) {
            properties.load(input);
        }
​
        // 打印所有属性
        properties.forEach((key, value) -> System.out.println(key + ": " + value));
    }
}

底层结构

  • Properties 的基础是 Hashtable

    • 底层采用线程安全的哈希表结构。

    • 键和值均为字符串类型(String),以适应配置文件的存储和解析需求。

    • 提供了 load()store() 方法,用于流式操作,方便配置文件的读写。

优点

  1. 简单直观:内置方法支持直接操作配置文件,减少手动解析的复杂性;适合存储和管理小规模配置。

  2. 线程安全:继承自 Hashtable,所有操作均是同步的,适合简单的多线程环境。

  3. 与文件系统集成良好:提供了流式操作接口,方便将键值对直接保存为 .properties 文件或从文件中加载。

缺点

  1. 性能较低:由于继承自同步的 Hashtable,在现代高并发场景下不推荐使用,性能落后于 HashMap

  2. 局限性:仅支持 String 类型的键值对,若需要存储复杂对象,需额外序列化。

  3. 不适合大规模配置:适合小型项目或简单模块的配置管理,大型系统建议采用更复杂的配置管理工具(如 Apache Commons Configuration 或 Spring)。

总结

集合类底层数据结构主要优点主要缺点
ArrayList动态数组查询效率高,支持随机访问插入删除效率低,扩容代价大
LinkedList双向链表插入删除效率高,内存灵活查询效率低,占用内存大
HashSet哈希表查询和去重效率高无序存储,受哈希冲突影响
LinkedHashSet哈希表 + 双向链表有序存储,去重效率高内存占用较高
TreeSet红黑树有序存储,按自然顺序或自定义顺序排序性能略低于哈希表,操作复杂度为 O(log n)
HashMap哈希表查找和插入效率高,支持键值对映射无序存储,受哈希冲突影响
LinkedHashMap哈希表 + 双向链表有序存储,按插入顺序遍历键值对内存开销较高
TreeMap红黑树有序存储,按键自然顺序或自定义顺序排序性能略低于哈希表,操作复杂度为 O(log n)
Properties哈希表(继承自 Hashtable专为存储键值对配置设计,支持读写 .properties 文件性能较低(继承自同步的 Hashtable),不适合高并发

知识点补充

1. 什么是哈希表?

哈希表是一种用来存储 键值对 的工具,它能非常快速地找到数据。你可以把它想象成一个 带编号的储物柜,每个柜子都有一个编号(索引),你把东西存进去时,会根据物品的特点计算出一个编号,然后直接放进对应的柜子里。取东西时也用相同的方法计算编号,直接找到对应的柜子打开拿走。

例子

  • 假如你有一本字典,查找某个单词(键)对应的解释(值)。

  • 传统查找方法:逐页翻阅,耗时长。

  • 使用哈希表:计算单词的编号,直接跳到对应的位置查看解释,速度非常快。

总结类比

  • 键是 “单词”,值是 “解释”。

  • 哈希表通过哈希函数快速找到这个单词在哪页。


2. 哈希表是如何存数据的?

哈希表的核心在于 哈希函数。这个函数就像一个计算器,可以把一个键(比如一个字符串)变成一个数字(哈希值)。哈希值用来确定数据存储的位置。

步骤:

  1. 计算位置:用哈希函数把键变成一个数字,然后对储物柜的总数取模(%),确定放在哪个柜子里。假如储物柜有 10 个,"Alice" 的哈希值是 42,42 % 10 = 2,所以数据放到第 2 个柜子。

  2. 存储值:把数据放到计算出的柜子里。

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

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

相关文章

acitvemq AMQP:因为消息映射策略配置导致的MQTT接收JMS消息乱码问题 x-opt-jms-dest x-opt-jms-msg-type

使用ActiveMQ&#xff08;5.14.5&#xff09;作消息系统服务的场景下&#xff0c; 当我用Apache Qpid Proton发送消息(使用AMQP协议)发送JMS消息&#xff0c;用Paho MQTT接收消息的时候&#xff0c; 收到的消息前面总是有一串乱码&#xff0c;大概就是这样&#xff1a; 4Sp?AS…

apisix的hmac-auth认证

目录 1、apisix的hmac认证Authorization头信息 2、signature的lua生成源码 3、java生成签证的简单示例 4、postman调用如下 apisix的hmac-auth认证&#xff0c;介绍可以看官方文档 hmac-auth | Apache APISIX -- Cloud-Native API Gateway 照着官方文档&#xff0c;发现生…

Webpack在Vue CLI中的应用

webpack 作为目前最流行的项目打包工具&#xff0c;被广泛使用于项目的构建和开发过程中&#xff0c;其实说它是打包工具有点大材小用了&#xff0c;我个人认为它是一个集前端自动化、模块化、组件化于一体的可拓展系统&#xff0c;你可以根据自己的需要来进行一系列的配置和安…

uniapp 前端解决精度丢失的问题 (后端返回分布式id)

原因&#xff1a; 后端使用分布式id, id为19位数&#xff0c;导致精度丢失 &#xff0c;前端解决方法 这个是通过浏览器请求回来的数据&#xff0c;这个时候id 数据已经丢失了&#xff0c;在数据库查询不到&#xff0c;在调获详情接口的时候会有问题 实际的&#xff1a; 解决…

外网访问 Docker 容器的可视化管理工具 DockerUI

DockerUI 是一个 docker 容器镜像的可视化图形化管理工具&#xff0c;DockerUI 可以用来轻松构建、管理和维护 docker 环境。让用户维护起来更方便。 本文就介绍如何安装使用 DockerUI 并结合路由侠内网穿透来访问 DockerUI。 第一步&#xff0c;安装 DockerUI 1&#xff0c;…

Docker安装和使用RabbitMQ

Docker安装和使用RabbitMQ 1、拉取镜像2、运行Docker容器3、控制台界面 1、拉取镜像 docker pull rabbitmq:3-management2、运行Docker容器 docker run \-e RABBITMQ_DEFAULT_USERjagochan \-e RABBITMQ_DEFAULT_PASSjagochan \--name mq \--hostname mq \-p 15672:15672 \-p…

STM32F103RCT6学习之五:ADC

1.ADC基础 ADC&#xff08;Analog-Digital Converter&#xff09;模拟-数字转换器ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量&#xff0c;建立模拟电路到数字电路的桥梁12位逐次逼近型ADC&#xff0c;1us转换时间 输入电压范围&#xff1a;0~3.3V&#xff…

Python学习(2):注释、数字、文本、列表

1 关于注释 Python 使用井号#作为单行注释的符号&#xff0c; 使用三个连续的单引号’’或者三个连续的双引号"""注释多行内容。 2 数字 2.1 基本运算 解释器像一个简单的计算器&#xff1a;你可以输入一个表达式&#xff0c;它将给出结果值。 表达式语法很直观…

【Maven_bugs】The project main artifact does not exist

背景&#xff1a;我想使用 maven-shade-plugin 打一个 fat jar 时报了标题中的错误&#xff0c;使用的命令是&#xff1a;org.apache.maven.plugins:maven-shade-plugin:shade -pl :shade-project。项目结构如下图&#xff0c;我想把子模块 shade-project 打成一个 fat jar&…

Wordperss漏洞 DeDeCMS漏洞

Wordperss漏洞 环境搭建 #执⾏命令 cd /vulhub/wordpress/pwnscriptum docker-compose up -d #靶场地址 http://8.155.7.173:8080/wp-admin/ 注册账号 登录 漏洞一&#xff1a;后台修改模板拿WebShell 步骤一&#xff1a;思路是修改其WP的模板写入⼀句话木马后门并访问其文件…

使用 HTML 和 CSS 实现绚丽的节日烟花效果

文章目录 1. 效果预览2. 核心技术栈3. 核心代码解读3.1 HTML结构3.2 霓虹文字的CSS样式3.2.1 核心样式代码3.2.2 动画效果 3.3 JavaScript 的烟花效果实现3.3.1 烟花上升3.3.2 粒子爆炸 4. 用户交互5. 运行步骤总结 1. 效果预览 打开后输入文本的展示内容 用户点击页面后播放…

电力场景输电线路异物检测数据集VOC+YOLO格式4370张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4370 标注数量(xml文件个数)&#xff1a;4370 标注数量(txt文件个数)&#xff1a;4370 …

【Next.js】002-路由篇|App Router

【Next.js】002-路由篇|App Router 文章目录 【Next.js】002-路由篇|App Router一、前言二、文件系统&#xff08;file-system&#xff09;1、说明2、演练创建代码运行访问让 Cursor 分析错误别偷懒&#xff0c;还是探究一下 Pages Router 方式吧创建代码运行并访问项目/about …

【FPGA开发】ZYNQ中PS与PL交互操作总结、BRAM,DMA原理浅析、仿真操作

文章目录 PL与PS交互综述交互端口性能&特点&#xff08;选择方案的凭据&#xff09;GPIO-AXI_GPDMA-DMACHP-AXI_HPACP-AXI_ACP 数据交互实验GP通过BRAMPS为主机&#xff0c;读写BRAMPL作为主机&#xff0c;读写BRAM DMA方式交互 PL与PS交互综述 网络上关于PS PL交互的教程…

构建全志 T113 Tina SDK

1、环境配置&#xff1a; 准备一个 Ubuntu 系统&#xff0c;可以是 WSL&#xff0c;虚拟机等&#xff0c;建议版本是 20.04。 1.1、安装必要的软件 进入系统后&#xff0c;输入下方命令安装需要的工具 &#xff1a; sudo apt update -y sudo apt full-upgrade -y sudo apt i…

Linux 搭建 nginx+keepalived 高可用 | Nginx反向代理

注意&#xff1a;本文为 “Linux 搭建 nginxkeepalived (主备双主模式) 高可用 | Nginx反向代理” 相关文章合辑。 KeepalivedNginx实现高可用&#xff08;HA&#xff09; xyang0917 于 2016-09-17 00:24:15 发布 keepalived 的 HA 分为抢占模式和非抢占模式&#xff0c;抢占…

RDFS—RDF模型属性扩展解析

目录 前言1. 什么是RDFS&#xff1f;1.1 RDFS的核心概念1.2 RDFS与RDF的区别 2. RDFS的基础概念2.1 类&#xff08;Class&#xff09;2.2 属性&#xff08;Property&#xff09;2.3 关系&#xff08;Relation&#xff09;2.4 定义域&#xff08;Domain&#xff09;2.5 值域&…

rust windwos 两个edit框

use winapi::shared::minwindef::LOWORD; use windows::{core::*,Win32::{Foundation::*,Graphics::Gdi::{BeginPaint, EndPaint, PAINTSTRUCT},System::LibraryLoader::GetModuleHandleA,UI::WindowsAndMessaging::*,}, };// 两个全局静态变量&#xff0c;用于保存 Edit 控件的…

Python Polars快速入门指南:LazyFrames

前文已经介绍了Polars的Dataframe, Contexts 和 Expressions&#xff0c;本文继续介绍Polars的惰性API。惰性API是该库最强大的功能之一&#xff0c;使用惰性API可以设定一系列操作&#xff0c;而无需立即运行它们。相反&#xff0c;这些操作被保存为计算图&#xff0c;只在必要…

常见CMS漏洞(wordpress,DedeCms,ASPCMS,PHPMyAdmin)

练习一:wordpress漏洞测试 1.上传包含木马的主题 安装网站 登陆网站 上传主题处测试漏洞注入 在上传主题模板压缩包中写入一句话木马 网站上传含有木马的zip压缩包 上传成功 wordpress主题目录 wp-content/themes 访问上传木马测试 2.修改主题的配置文件为含有木马的文件 练习…