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

在 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. 存储值:把数据放到计算出的柜子里。

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

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

相关文章

[2029].第6-06节:MyISAM引擎中的索引与 InnoDB引擎中的索引对比

所有博客大纲 后端学习大纲 MySQL学习大纲 1.MyISAM索引&#xff1a; 1.1.B树索引适用存储引擎&#xff1a; 1.B树索引适用存储引擎如下表所示&#xff1a; 2.即使多个存储引擎都支持同一种类型的B树索引&#xff0c;但它们的实现原理也是不同的 Innodb和MyISAM默认的索引是B…

DS的使用

使用DS和[address]实现字的传送 要解决的问题:CPU从内存单元中要读取数据 要求&#xff1a;CPU要读取一个内存单元的时候&#xff0c;必须先给出这个内存单元的地址。 原理&#xff1a;在8086PC中&#xff0c;内存地址段地址和偏移地址组成(段地址:偏移地址) 解决方案 :DS和[a…

使用RKNN进行YOLOv8人体姿态估计的实战教程:yolov8-pose.onnx转yolov8-pose.rknn+推理全流程

之前文章有提到“YOLOv8的原生模型包含了后处理步骤,其中一些形状超出了RK3588的矩阵计算限制,因此需要对输出层进行一些裁剪”,通过裁剪后得到的onnx能够顺利的进行rknn转换,本文将对转rnkk过程,以及相应的后处理进行阐述。并在文末附上全部源码、数据、模型的百度云盘链…

短视频矩阵系统后端源码搭建实战与技术详解,支持OEM

一、引言 随着短视频行业的蓬勃发展&#xff0c;短视频矩阵系统成为了众多企业和创作者进行多平台内容运营的有力工具。后端作为整个系统的核心支撑&#xff0c;负责处理复杂的业务逻辑、数据存储与交互&#xff0c;其搭建的质量直接影响着系统的性能、稳定性和可扩展性。本文将…

JS 设置按钮的loading效果

本文是在其他博主的博客JS学习笔记 | 遮罩层Loading实现_jsp loading-CSDN博客基础上&#xff0c;进行实践的。 目录 一、需求 二、Jspcss实现代码 一、需求 在springboot项目中的原始html5页面中&#xff0c;原本的功能是页面加载时&#xff0c;使用ajax向后端发送请求&…

用VBA将word文档处理成支持弹出式注释的epub文档可用的html内容

有一种epub文件&#xff0c;其中的注释以弹窗形式显示&#xff0c;如下图&#xff1a; 点击注释引用后&#xff0c;对应的注释内容会弹出在页面中显示&#xff0c;再次点击弹窗外的任意位置该弹窗即关闭&#xff0c;关闭后点击任意注释引用&#xff0c;对应的注释内容会弹窗显示…

实践KDTS-WEB从mysql迁移到kingbasev9

数据库国产化替代数据迁移是一个复杂且关键的过程。这涉及到将原有数据库中的数据准确、完整地迁移到新的国产数据库中&#xff0c;同时确保数据的完整性和一致性。人大金仓提供了强大的数据库迁移工具&#xff08;KDTS&#xff09;对同构、异构数据库数据迁移&#xff1b; 数…

多旋翼无人机理论 | 四旋翼动力学数学模型与Matlab仿真

多旋翼无人机理论 | 四旋翼动力学数学模型与Matlab仿真 力的来源数学模型数学模型总结Matlab 仿真 力的来源 无人机的动力系统&#xff1a;电调-电机-螺旋桨 。 给人最直观的感受就是 电机带动螺旋桨转&#xff0c;产生升力。 螺旋桨旋转产生升力的原因&#xff0c;在很多年…

为什么要在PHY芯片和RJ45网口中间加网络变压器

在PHY芯片和RJ45网口之间加入网络变压器是出于以下几个重要的考虑&#xff1a; 1. 电气隔离&#xff1a;网络变压器提供了电气隔离功能&#xff0c;有效阻断了PHY芯片与RJ45之间直流分量的直接连接。这样可以防止可能的电源冲突&#xff0c;降低系统故障的风险&#xff0c;并保…

Windows 安装 Jenkins 教程

Jenkins 简介 Jenkins 是一个开源的自动化服务器&#xff0c;主要用于持续集成&#xff08;CI&#xff09;和持续交付&#xff08;CD&#xff09;。它可以自动化软件开发生命周期中的许多任务&#xff0c;如构建、测试、部署和发布。Jenkins 最初是由 Kohsuke Kawaguchi 在 20…

Docker中的MYSQL导入本地SQL语句

在本地mysql安装的bin目录下打开cmd窗口并执行以下命令导出sql文件 mysqldump -uroot -p mysql >schema.sql mysql -数据库 schema.sql -导出的SQL语句文件名 使用xftp上传文件到centos7中的某个文件夹中 使用docker cp schema.sql mysql:.(有一个点&#xff09;上传到mys…

javaweb 04 springmvc

0.1 在上一次的课程中&#xff0c;我们开发了springbootweb的入门程序。 基于SpringBoot的方式开发一个web应用&#xff0c;浏览器发起请求 /hello 后 &#xff0c;给浏览器返回字符串 “Hello World ~”。 其实呢&#xff0c;是我们在浏览器发起请求&#xff0c;请求了我们…

LinkedList类 (链表)

目录 一. LinkedList 基本介绍 二. LinkedList 中的法及其应用 1. 添加元素 (1) add() (2) addAll() (3) addFirst() (4) addLast() 2. 删除元素 (1) remove() (2) removeAll() (3) removeFirst() (4) removeLast() 3. 遍历元素 (1) for 循环遍历 (2) for - each …

Python毕业设计选题:基于Python的社区爱心养老管理系统设计与实现_django

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 用户管理 身体健康界面 公共书籍界面 借阅信息界面 归还…

第T4周:TensorFlow实现猴痘识别(Tensorboard的使用)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目标&#xff1a; 1、学习tensorboard的使用 具体实现&#xff1a; &#xff08;一&#xff09;环境&#xff1a; 语言环境&#xff1a;Python 3.10 编 译 器…

Gitlab17.7+Jenkins2.4.91实现Fastapi/Django项目持续发布版本详细操作(亲测可用)

一、gitlab设置&#xff1a; 1、进入gitlab选择主页在左侧菜单的下面点击管理员按钮。 2、选择左侧菜单的设置&#xff0c;选择网络&#xff0c;在右侧选择出站请求后选择允许来自webhooks和集成对本地网络的请求 3、webhook设置 进入你自己的项目选择左侧菜单的设置&#xff…

嵌入式硬件杂谈(七)IGBT MOS管 三极管应用场景与区别

引言&#xff1a;在现代嵌入式硬件设计中&#xff0c;开关元件作为电路中的重要组成部分&#xff0c;起着至关重要的作用。三种主要的开关元件——IGBT&#xff08;绝缘栅双极型晶体管&#xff09;、MOSFET&#xff08;金属氧化物半导体场效应晶体管&#xff09;和三极管&#…

Kafka数据迁移全解析:同集群和跨集群

文章目录 一、同集群迁移二、跨集群迁移 Kafka两种迁移场景&#xff0c;分别是同集群数据迁移、跨集群数据迁移。 一、同集群迁移 应用场景&#xff1a; broker 迁移 主要使用的场景是broker 上线,下线,或者扩容等.基于同一套zookeeper的操作。 实践&#xff1a; 将需要新添加…

我的秋招总结

我的秋招总结 个人背景 双非本&#xff0c;985硕&#xff0c;科班 准备情况 以求职为目的学习Java的时间大概一年。 八股&#xff0c;一开始主要是看B站黑马的八股文课程&#xff0c;背JavaGuide和小林coding还有面试鸭。 算法&#xff0c;250&#xff0c;刷了3遍左右 项目&…

构建全志 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…