Java 编码系列:集合框架(List、Set、Map 及其常用实现类)
引言
在 Java 开发中,集合框架是不可或缺的一部分,它提供了存储和操作一组对象的工具。Java 集合框架主要包括 List、Set 和 Map 接口及其常用的实现类。正确理解和使用这些集合类不仅可以提高代码的可读性和性能,还能避免一些常见的错误。本文将深入探讨 Java 集合框架的底层原理,并结合大厂的最佳实践,帮助读者掌握这些核心概念。
1. List 接口及其常用实现类
1.1 基本概念
List 接口表示一个有序的集合,允许重复元素。List 中的每个元素都有一个索引,可以通过索引来访问和修改元素。
1.2 常用实现类
ArrayList:基于动态数组实现,支持快速随机访问,但在中间位置插入或删除元素时性能较差。LinkedList:基于双向链表实现,支持快速插入和删除操作,但在随机访问时性能较差。Vector:类似于ArrayList,但线程安全,性能较差。Stack:继承自Vector,实现了栈数据结构。
1.3 底层原理
-
ArrayList:- 内部实现:
ArrayList内部使用一个动态数组Object[] elementData来存储元素。 - 扩容机制:当数组容量不足时,会自动扩容,新容量通常是原容量的 1.5 倍。
ArrayList<String> list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("C");System.out.println(list); // [A, B, C] System.out.println(list.get(1)); // B - 内部实现:
-
LinkedList:- 内部实现:
LinkedList内部使用双向链表来存储元素,每个节点包含一个元素值、一个指向前一个节点的引用和一个指向后一个节点的引用。 - 插入和删除:在链表头部或尾部插入和删除元素的时间复杂度为 O(1),但在中间位置插入和删除元素的时间复杂度为 O(n)。
LinkedList<String> list = new LinkedList<>(); list.addFirst("A"); list.addLast("C"); list.add(1, "B");System.out.println(list); // [A, B, C] System.out.println(list.get(1)); // B - 内部实现:
1.4 常见操作
-
添加元素:
add(E e):在列表末尾添加元素。add(int index, E e):在指定位置插入元素。
ArrayList<String> list = new ArrayList<>(); list.add("A"); list.add(0, "B");System.out.println(list); // [B, A] -
删除元素:
remove(int index):删除指定位置的元素。remove(Object o):删除第一个匹配的元素。
ArrayList<String> list = new ArrayList<>(Arrays.asList("A", "B", "C")); list.remove(1); list.remove("C");System.out.println(list); // [A] -
获取元素:
get(int index):获取指定位置的元素。
ArrayList<String> list = new ArrayList<>(Arrays.asList("A", "B", "C")); String element = list.get(1);System.out.println(element); // B -
遍历元素:
- 使用
for循环: - 使用
Iterator:
ArrayList<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));// 使用 for 循环 for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i)); }// 使用 Iterator Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) {System.out.println(iterator.next()); } - 使用
1.5 最佳实践
-
选择合适的实现类:
ArrayList:适用于需要频繁随机访问的场景。LinkedList:适用于需要频繁插入和删除操作的场景。
-
初始化容量:
ArrayList:如果已知元素数量,可以初始化容量,减少扩容次数。
ArrayList<String> list = new ArrayList<>(100); -
使用
List接口:- 代码复用:在方法签名中使用
List接口,而不是具体的实现类,提高代码的复用性。
public void printList(List<String> list) {for (String s : list) {System.out.println(s);} } - 代码复用:在方法签名中使用
2. Set 接口及其常用实现类
2.1 基本概念
Set 接口表示一个不包含重复元素的集合。Set 不保证元素的顺序,也不允许插入重复元素。
2.2 常用实现类
HashSet:基于哈希表实现,不保证元素的顺序,不允许重复元素。LinkedHashSet:基于哈希表和链表实现,保持元素的插入顺序,不允许重复元素。TreeSet:基于红黑树实现,保持元素的自然顺序或自定义排序,不允许重复元素。
2.3 底层原理
-
HashSet:- 内部实现:
HashSet内部使用HashMap来存储元素,键为元素本身,值为一个常量对象。 - 哈希冲突:通过哈希函数计算元素的哈希值,如果发生哈希冲突,使用链地址法解决。
HashSet<String> set = new HashSet<>(); set.add("A"); set.add("B"); set.add("C");System.out.println(set); // [A, B, C] - 内部实现:
-
LinkedHashSet:- 内部实现:
LinkedHashSet内部使用LinkedHashMap来存储元素,保持元素的插入顺序。 - 链表:每个元素不仅有一个哈希桶,还有一个双向链表节点。
LinkedHashSet<String> set = new LinkedHashSet<>(); set.add("A"); set.add("B"); set.add("C");System.out.println(set); // [A, B, C] - 内部实现:
-
TreeSet:- 内部实现:
TreeSet内部使用TreeMap来存储元素,保持元素的自然顺序或自定义排序。 - 红黑树:
TreeSet使用红黑树实现,保证元素的有序性。
TreeSet<String> set = new TreeSet<>(); set.add("C"); set.add("A"); set.add("B");System.out.println(set); // [A, B, C] - 内部实现:
2.4 常见操作
-
添加元素:
add(E e):添加元素,如果元素已存在,则不添加。
HashSet<String> set = new HashSet<>(); set.add("A"); set.add("B"); set.add("A"); // 不会添加重复元素System.out.println(set); // [A, B] -
删除元素:
remove(Object o):删除指定的元素。
HashSet<String> set = new HashSet<>(Arrays.asList("A", "B", "C")); set.remove("B");System.out.println(set); // [A, C] -
检查元素:
contains(Object o):检查集合中是否包含指定的元素。
HashSet<String> set = new HashSet<>(Arrays.asList("A", "B", "C")); boolean contains = set.contains("B");System.out.println(contains); // true -
遍历元素:
- 使用
for-each循环: - 使用
Iterator:
HashSet<String> set = new HashSet<>(Arrays.asList("A", "B", "C"));// 使用 for-each 循环 for (String s : set) {System.out.println(s); }// 使用 Iterator Iterator<String> iterator = set.iterator(); while (iterator.hasNext()) {System.out.println(iterator.next()); } - 使用
2.5 最佳实践
-
选择合适的实现类:
HashSet:适用于不需要保持元素顺序的场景。LinkedHashSet:适用于需要保持元素插入顺序的场景。TreeSet:适用于需要保持元素有序的场景。
-
使用
Set接口:- 代码复用:在方法签名中使用
Set接口,而不是具体的实现类,提高代码的复用性。
public void printSet(Set<String> set) {for (String s : set) {System.out.println(s);} } - 代码复用:在方法签名中使用
-
重写
equals和hashCode方法:- 确保唯一性:如果自定义对象需要存储在
Set中,必须重写equals和hashCode方法,确保对象的唯一性。
class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return age == person.age && Objects.equals(name, person.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);} } - 确保唯一性:如果自定义对象需要存储在
3. Map 接口及其常用实现类
3.1 基本概念
Map 接口表示一个键值对的集合,每个键最多只能映射到一个值。Map 不保证键值对的顺序,除非使用特定的实现类。
3.2 常用实现类
HashMap:基于哈希表实现,不保证键值对的顺序,允许一个null键和多个null值。LinkedHashMap:基于哈希表和链表实现,保持键值对的插入顺序,允许一个null键和多个null值。TreeMap:基于红黑树实现,保持键值对的自然顺序或自定义排序,不允许null键。Hashtable:类似于HashMap,但线程安全,性能较差,不允许null键和null值。
3.3 底层原理
-
HashMap:- 内部实现:
HashMap内部使用一个数组Node<K,V>[] table来存储键值对,每个节点包含一个键、一个值、一个哈希值和一个指向下一个节点的引用。 - 哈希冲突:通过哈希函数计算键的哈希值,如果发生哈希冲突,使用链地址法解决。
HashMap<String, Integer> map = new HashMap<>(); map.put("A", 1); map.put("B", 2); map.put("C", 3);System.out.println(map); // {A=1, B=2, C=3} - 内部实现:
-
LinkedHashMap:- 内部实现:
LinkedHashMap内部使用HashMap和双向链表来存储键值对,保持键值对的插入顺序。 - 链表:每个节点不仅有一个哈希桶,还有一个双向链表节点。
LinkedHashMap<String, Integer> map = new LinkedHashMap<>(); map.put("A", 1); map.put("B", 2); map.put("C", 3);System.out.println(map); // {A=1, B=2, C=3} - 内部实现:
-
TreeMap:- 内部实现:
TreeMap内部使用TreeMap来存储键值对,保持键值对的自然顺序或自定义排序。 - 红黑树:
TreeMap使用红黑树实现,保证键值对的有序性。
TreeMap<String, Integer> map = new TreeMap<>(); map.put("C", 3); map.put("A", 1); map.put("B", 2);System.out.println(map); // {A=1, B=2, C=3} - 内部实现:
3.4 常见操作
-
添加键值对:
put(K key, V value):添加键值对,如果键已存在,则更新对应的值。
HashMap<String, Integer> map = new HashMap<>(); map.put("A", 1); map.put("B", 2); map.put("A", 3); // 更新键 "A" 的值System.out.println(map); // {A=3, B=2} -
删除键值对:
remove(Object key):删除指定键的键值对。
HashMap<String, Integer> map = new HashMap<>(Map.of("A", 1, "B", 2, "C", 3)); map.remove("B");System.out.println(map); // {A=1, C=3} -
获取值:
get(Object key):获取指定键的值。
HashMap<String, Integer> map = new HashMap<>(Map.of("A", 1, "B", 2, "C", 3)); int value = map.get("B");System.out.println(value); // 2 -
检查键或值:
containsKey(Object key):检查集合中是否包含指定的键。containsValue(Object value):检查集合中是否包含指定的值。
HashMap<String, Integer> map = new HashMap<>(Map.of("A", 1, "B", 2, "C", 3)); boolean containsKey = map.containsKey("B"); boolean containsValue = map.containsValue(2);System.out.println(containsKey); // true System.out.println(containsValue); // true -
遍历键值对:
- 使用
for-each循环: - 使用
Iterator:
HashMap<String, Integer> map = new HashMap<>(Map.of("A", 1, "B", 2, "C", 3));// 使用 for-each 循环 for (Map.Entry<String, Integer> entry : map.entrySet()) {System.out.println(entry.getKey() + ": " + entry.getValue()); }// 使用 Iterator Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator(); while (iterator.hasNext()) {Map.Entry<String, Integer> entry = iterator.next();System.out.println(entry.getKey() + ": " + entry.getValue()); } - 使用
3.5 最佳实践
-
选择合适的实现类:
HashMap:适用于不需要保持键值对顺序的场景。LinkedHashMap:适用于需要保持键值对插入顺序的场景。TreeMap:适用于需要保持键值对有序的场景。
-
使用
Map接口:- 代码复用:在方法签名中使用
Map接口,而不是具体的实现类,提高代码的复用性。
public void printMap(Map<String, Integer> map) {for (Map.Entry<String, Integer> entry : map.entrySet()) {System.out.println(entry.getKey() + ": " + entry.getValue());} } - 代码复用:在方法签名中使用
-
重写
equals和hashCode方法:- 确保唯一性:如果自定义对象作为键存储在
Map中,必须重写equals和hashCode方法,确保键的唯一性。
class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return age == person.age && Objects.equals(name, person.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);} } - 确保唯一性:如果自定义对象作为键存储在
4. 大厂最佳实践
4.1 集合框架
-
阿里巴巴《Java开发手册》:
- 使用
ArrayList而不是LinkedList:除非确实需要频繁的插入和删除操作,否则使用ArrayList。 - 初始化容量:如果已知元素数量,可以初始化容量,减少扩容次数。
- 使用
Set接口:在方法签名中使用Set接口,而不是具体的实现类,提高代码的复用性。
- 使用
-
Google Java Style Guide:
- 使用
for-each循环:在遍历集合时,优先使用for-each循环,提高代码的可读性。 - 使用
Map接口:在方法签名中使用Map接口,而不是具体的实现类,提高代码的复用性。
- 使用
-
Oracle 官方文档:
- 重写
equals和hashCode方法:如果自定义对象需要存储在集合中,必须重写equals和hashCode方法,确保对象的唯一性。 - 选择合适的实现类:根据具体需求选择合适的集合实现类,提高代码的性能。
- 重写
5. 总结
本文深入探讨了 Java 集合框架的 List、Set 和 Map 接口及其常用实现类的底层原理,并结合大厂的最佳实践,帮助读者掌握这些核心概念。正确理解和使用这些集合类不仅可以提高代码的可读性和性能,还能避免一些常见的错误。希望本文对你有所帮助,如果你有任何问题或建议,欢迎留言交流。
希望这篇文章能够满足你的需求,如果有任何进一步的问题或需要更多内容,请随时告诉我!
相关文章:
Java 编码系列:集合框架(List、Set、Map 及其常用实现类)
引言 在 Java 开发中,集合框架是不可或缺的一部分,它提供了存储和操作一组对象的工具。Java 集合框架主要包括 List、Set 和 Map 接口及其常用的实现类。正确理解和使用这些集合类不仅可以提高代码的可读性和性能,还能避免一些常见的错误。本…...
Go进阶概览 -【7.2 泛型的使用与实现分析】
7.2 泛型的使用与实现分析 泛型是Go 1.18引入的概念,在引入这个概念前经过了好几年的考量最终才将这这个特性加进去。 泛型在多种语言中都是存在的,比如C、Java等语言中都有泛型的概念。 本节我们将针对泛型的使用、实现原理进行整体的讲解。 本节代…...
罗德岛战记游戏源码(客户端+服务端+数据库+全套源码)游戏大小9.41G
罗德岛战记游戏源码(客户端服务端数据库全套源码)游戏大小9.41G 下载地址: 通过网盘分享的文件:【源码】罗德岛战记游戏源码(客户端服务端数据库全套源码)游戏大小9.41G 链接: https://pan.baidu.com/s/1y0…...
AI+教育|拥抱AI智能科技,让课堂更生动高效
AI在教育领域的应用正逐渐成为现实,提供互动性强的学习体验,正在改变传统教育模式。AI不仅改变了传统的教学模式,还为教育提供了更多的可能性和解决方案。从个性化学习体验到自动化管理任务,AI正在全方位提升教育质量和效率。随着…...
WebServer
一、服务器代码 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <unistd.h> #define PORT 80 #define BUFFER_SIZE 1024 void ha…...
java项目之基于spring boot的多维分类的知识管理系统的设计与实现源码
项目简介 基于spring boot的多维分类的知识管理系统的设计与实现实现了以下功能: 基于spring boot的多维分类的知识管理系统的设计与实现的主要使用者管理员可以管理用户信息,知识分类,知识信息等,用户可以查看和下载管理员发布…...
go的结构体、方法、接口
结构体: 结构体:不同类型数据集合 结构体成员是由一系列的成员变量构成,这些成员变量也被称为“字段” 先声明一下我们的结构体: type Person struct {name stringage intsex string } 定义结构体法1: var p1 P…...
力扣第一题——删除有序数组中的重复项
给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次,返回删除后数组的新长度。不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1)额外空间的条件下完成。 示例 1&#x…...
Tuxera NTFS for Mac 2023绿色版
在数字化时代,数据的存储和传输变得至关重要。Mac用户经常需要在Windows NTFS格式的移动硬盘上进行读写操作,然而,由于MacOS系统默认不支持NTFS的写操作,这就需要我们寻找一款高效的读写软件。Tuxera NTFS for Mac 2023便是其中…...
LeetCode[中等] 155. 最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。void pop() 删除堆栈顶部的元素。int top() 获取堆栈顶部的元素。int get…...
Python青少年简明教程目录
Python青少年简明教程目录 学习编程语言时,会遇到“开头难”和“深入难”的问题,这是许多编程学习者都会经历的普遍现象。 学习Python对于青少年来说是一个很好的编程起点,相对容易上手入门,但语言特性复杂,应用较广&…...
Revit学习记录-版本2018【持续补充】
将墙面拆分并使用不同材料 【修改】>【几何图形】>【拆分面】(SF), 然后再使用【修改】>【几何图形】>【填色】(PT)进行填充 楼板漏在墙外 绘制楼板时最好选择墙体绘制,如果标高上不显示墙体,可以先选…...
深度学习01-概述
深度学习是机器学习的一个子集。机器学习是实现人工智能的一种途径,而深度学习则是通过多层神经网络模拟人类大脑的方式进行学习和知识提取。 深度学习的关键特点: 1. 自动提取特征:与传统的机器学习方法不同,深度学习不需要手动…...
leetcode232. 用栈实现队列
leetcode232. 用栈实现队列 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty): 实现 MyQueue 类: void push(int x) 将元素 x 推到队列的末尾 int pop() 从队列的开头移除并返回元…...
智慧火灾应急救援航拍检测数据集(无人机视角)
智慧火灾应急救援。 无人机,直升机等航拍视角下火灾应急救援检测数据集,数据分别标注了火,人,车辆这三个要素内容,29810张高清航拍影像,共31GB,适合森林防火,应急救援等方向的学术研…...
eureka.client.service-url.defaultZone的坑
错误的配置 eureka: client: service-url: default-zone: http://192.168.100.10:8080/eureka正确的配置 eureka: client: service-url: defaultZone: http://192.168.100.10:8080/eureka根据错误日志堆栈打断电调试 出现两个key,也就是defaultZone不支持snake-c…...
统信服务器操作系统【d版字符系统升级到dde图形化】配置方法
统信服务器操作系统d版本上由字符系统升级到 dde 桌面系统的过程 文章目录 一、准备环境二、功能描述安装步骤1. lightdm 安装2. dde 安装 一、准备环境 适用版本:■UOS服务器操作系统d版 适用架构:■ARM64、AMD64、MIPS64 网络:连接互联网…...
学习IEC 62055付费系统标准
1.IEC 62055 国际标准 IEC 62055 是目前关于付费系统的唯一国际标准,涵盖了付费系统、CIS 用户信息系统、售电系统、传输介质、数据传输标准、预付费电能表以及接口标准等内容。 IEC 62055-21 标准化架构IEC 62055-31 1 级和 2 级有功预付费电能表IEC 62055-41 STS…...
如何在Markdown写文章上传到wordpress保证图片不丢失
如何在Markdown写文章上传到wordpress保证图片不丢失 写文日期,2023-11-16 引文 众所周知markdown是一款nb的笔记软件,本篇文章讲解如何在markdown编写文件后上传至wordpress论坛。并且保证图片不丢失(将图片上传至云端而非本地方法) 一&…...
html,css基础知识点笔记(二)
9.18(二) 本文主要教列表的样式设计 1)文本溢出 效果图 文字限制一行显示几个字,多余打点 line-height: 1.8em; white-space: nowrap; width: 40em; overflow: hidden; text-overflow: ellipsis;em表示一个文字的大小单位&…...
Qt5 super module多媒体模块详解:音频、视频、3D图形处理技术
Qt5 super module多媒体模块详解:音频、视频、3D图形处理技术 【免费下载链接】qt5 Qt5 super module 项目地址: https://gitcode.com/gh_mirrors/qt/qt5 Qt5 super module是一个功能强大的跨平台应用开发框架,其中的多媒体模块为开发者提供了全面…...
AI驱动的CNC闭环控制系统:边缘实时感知与控制实践
1. 项目概述:这不是在改装一台机床,而是在给金属加工装上“神经系统”“AI-Driven Machining: Building a Closed-Loop CNC System with IIoT Feedback (Building the CNC)”——这个标题里没有一个词是虚的。它不是在讲怎么用AI生成个加工路径图&#x…...
3分钟实现GitHub界面汉化:浏览器插件让GitHub说中文
3分钟实现GitHub界面汉化:浏览器插件让GitHub说中文 【免费下载链接】github-chinese GitHub 汉化插件,GitHub 中文化界面。 (GitHub Translation To Chinese) 项目地址: https://gitcode.com/gh_mirrors/gi/github-chinese 你是否曾因GitHub的英…...
STL专题三:list(2,关于list的若干问题)
1 迭代器细节问题大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点。在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。一个容…...
QMCDecode终极指南:5分钟快速掌握QQ音乐加密格式转换技巧
QMCDecode终极指南:5分钟快速掌握QQ音乐加密格式转换技巧 【免费下载链接】QMCDecode QQ音乐QMC格式转换为普通格式(qmcflac转flac,qmc0,qmc3转mp3, mflac,mflac0等转flac),仅支持macOS,可自动识别到QQ音乐下载目录,默…...
《从 0 实现 SGLang》第 1 篇 · LLM 推理引擎到底在做什么
千行代码,一步步搭出一个现代 LLM 推理引擎,吃透大模型推理的每一项关键技术。 本阶段目标 — 最简推理实现 用最朴素的方式把端到端推理跑通:先搭起整体框架,再逐个模块替换为完整实现。整个阶段共 5 篇短文: 序号…...
1987年4月26日下午15-17点出生性格、运势和命运
1987年4月24日晚上出生的人,如今已步入38岁的门槛。在职业生涯中,这是一个承上启下的关键阶段——既脱离了职场新人的青涩,又尚未到达管理者或专家的巅峰位置。从非命理的角度分析,他们的事业运势与时代变迁、个人选择和社会结构密…...
零极点分析:从系统稳定性到滤波器设计的核心工程工具
1. 项目概述:从“系统行为”的根源说起在信号处理、控制理论乃至电路设计的日常工作中,我们常常需要面对一个核心问题:如何预测、分析和设计一个系统的动态行为?无论是设计一个能稳定跟踪目标的控制器,还是优化一个音频…...
Halcon形状匹配实战:从`get_domain`到`add_channels`,手把手教你处理复杂背景下的目标定位
Halcon形状匹配实战:从get_domain到add_channels的工业级解决方案 在工业视觉检测中,目标定位的准确性直接影响着整个生产线的质量把控效率。当面对低对比度、复杂背景或干扰物密集的场景时,传统全图搜索策略往往表现不佳——这正是Halcon区域…...
ESXi安装卡在网卡识别?除了打驱动,你还可以试试这个国产替代方案FreeVM
ESXi网卡兼容性困境:为何国产FreeVM可能更适合你的虚拟化需求 当你第5次重启ESXi安装程序,屏幕上依然显示"No Network Adapters"的红色报错时,那种挫败感任何IT从业者都深有体会。硬件兼容性问题——这个困扰虚拟化领域多年的顽疾&…...
