Java 数据结构详解
Java 数据结构是 Java 编程中存储和组织数据的方式,就像现实生活中 “书架”“抽屉”“字典” 等工具 —— 不同的结构适合存放不同类型的数据,也决定了 “存、取、查” 的效率。Java 内置了一套完善的集合框架(Collection Framework),把常用数据结构封装成了易用的类,我们不需要从零实现,直接拿来用就行。
一、先搞懂:Java 集合框架的 “家谱”
Java 集合框架就像一个大家族,主要分为两大分支:
- 单值集合(Collection):每个元素是单个值(如数字、字符串),包括
List
(有序可重复)、Set
(无序不可重复); - 键值对集合(Map):每个元素是 “键 - 值” 对(如字典中的 “单词 - 解释”),代表是
Map
接口。
它们的关系可以简单理解为:
集合框架
├─ Collection(单值集合)
│ ├─ List(有序、可重复)
│ │ ├─ ArrayList(数组实现)
│ │ └─ LinkedList(链表实现)
│ └─ Set(无序、不可重复)
│ ├─ HashSet(哈希表实现)
│ └─ TreeSet(红黑树实现,有序)
└─ Map(键值对集合)
├─ HashMap(哈希表实现)
└─ TreeMap(红黑树实现,键有序)
二、单值集合(Collection)详解
1. List:有序可重复的 “数组增强版”
List
就像一排有编号的抽屉,每个抽屉有固定位置(索引),可以放相同的东西(可重复)。你可以通过编号快速找到某个抽屉,也可以在任意位置添加 / 删除抽屉。常用实现类:
-
ArrayList:底层是动态数组(长度可变的数组)。
- 优点:通过索引(如
get(0)
)访问元素极快(时间复杂度 O (1)); - 缺点:在中间插入 / 删除元素慢(需要移动后面的元素,时间复杂度 O (n));
- 类比:像一本书,查第 100 页很快,但在第 50 页插入一页,后面的页码都要变。
代码示例:import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { List<String> list = new ArrayList<>(); // 添加元素 list.add("苹果"); list.add("香蕉"); list.add("苹果"); // 允许重复 // 按索引访问(0 开始) System.out.println(list.get(1)); // 输出:香蕉 // 遍历 for (String fruit : list) { System.out.println(fruit); // 输出:苹果、香蕉、苹果(顺序和添加一致) } } }
- 优点:通过索引(如
-
LinkedList:底层是双向链表(每个元素像 “铁链” 一样连在一起,每个节点记录前后节点的位置)。
- 优点:在中间插入 / 删除元素快(只需改前后节点的链接,时间复杂度 O (1));
- 缺点:通过索引访问慢(需要从头遍历到目标位置,时间复杂度 O (n));
- 类比:像一串项链,拆中间的珠子只需解开两边的线,但要找第 100 颗珠子,得从第一颗开始数。
2. Set:无序不可重复的 “去重神器”
Set
就像一个没有编号的袋子,里面的元素没有固定顺序,且不能有重复(比如 “黑名单” 里不能有重复的名字)。常用实现类:
-
HashSet:底层是哈希表(通过 “哈希值” 快速定位元素)。
- 特点:无序(遍历顺序和添加顺序可能不同)、去重(依赖元素的
hashCode()
和equals()
方法); - 优点:添加、删除、查询速度极快(时间复杂度 O (1));
- 类比:像一本字典,通过拼音首字母(哈希值)快速找到单词,且不会有重复的单词。
代码示例:import java.util.HashSet; import java.util.Set; public class Test { public static void main(String[] args) { Set<String> set = new HashSet<>(); set.add("苹果"); set.add("香蕉"); set.add("苹果"); // 重复元素,会被自动过滤 // 遍历(顺序不确定) for (String fruit : set) { System.out.println(fruit); // 输出:苹果、香蕉(无重复) } } }
- 特点:无序(遍历顺序和添加顺序可能不同)、去重(依赖元素的
-
TreeSet:底层是红黑树(一种自平衡的排序树)。
- 特点:自动按元素大小排序(如数字从小到大、字符串按字典序)、去重;
- 优点:适合需要 “排序 + 去重” 的场景(如排行榜);
- 缺点:添加 / 查询速度比 HashSet 稍慢(时间复杂度 O (log n))。
三、键值对集合(Map):像字典一样 “通过键找值”
Map
存储的是 “键(Key)- 值(Value)” 对,就像字典中 “单词(键)- 解释(值)” 的对应关系。键不能重复(一个单词只能有一个解释),但值可以重复(多个单词可以有相同解释)。常用实现类:
-
HashMap:底层是哈希表(数组 + 链表 / 红黑树)。
- 特点:键无序、键唯一;
- 优点:根据键查询值的速度极快(O (1)),适合频繁的 “存、取” 操作;
- 类比:像手机通讯录,通过姓名(键)快速找到电话(值),姓名不能重复。
代码示例:import java.util.HashMap; import java.util.Map; public class Test { public static void main(String[] args) { Map<String, Integer> scoreMap = new HashMap<>(); // 存键值对 scoreMap.put("张三", 90); scoreMap.put("李四", 85); scoreMap.put("张三", 95); // 键重复,会覆盖之前的值 // 查值(通过键) System.out.println(scoreMap.get("李四")); // 输出:85 // 遍历所有键值对 for (Map.Entry<String, Integer> entry : scoreMap.entrySet()) { System.out.println(entry.getKey() + ":" + entry.getValue()); // 输出:张三:95、李四:85(顺序不确定) } } }
-
TreeMap:底层是红黑树。
- 特点:键会自动按大小排序(如数字键从小到大);
- 适合场景:需要按键排序的场景(如按学号排序的成绩表)。
四、其他常用数据结构
除了上述核心集合,Java 中还有一些特殊结构:
-
Queue(队列):“先进先出”(FIFO)的结构,像排队买票,先到先得。常用实现
LinkedList
(因为链表适合频繁添加 / 删除)。Queue<String> queue = new LinkedList<>(); queue.offer("第一个人"); // 入队 queue.offer("第二个人"); System.out.println(queue.poll()); // 出队:第一个人(先入先出)
-
Stack(栈):“后进先出”(LIFO)的结构,像叠盘子,最后放的先拿走。Java 中常用
Deque
替代(LinkedList
实现)。Deque<String> stack = new LinkedList<>(); stack.push("第一层盘子"); // 入栈 stack.push("第二层盘子"); System.out.println(stack.pop()); // 出栈:第二层盘子(后入先出)
-
数组(Array):最基础的线性结构,长度固定,通过索引访问快,但不能动态扩容(ArrayList 是对数组的 “动态扩容” 封装)。
五、如何选择合适的数据结构?
记住几个核心场景:
- 需有序 + 可重复 + 频繁查:用
ArrayList
; - 需有序 + 可重复 + 频繁增删中间元素:用
LinkedList
; - 需去重 + 无序:用
HashSet
; - 需去重 + 排序:用
TreeSet
; - 需键值对 + 无序:用
HashMap
; - 需键值对 + 键排序:用
TreeMap
; - 需先进先出:用
Queue
(LinkedList
实现); - 需后进先出:用
Deque
当栈(LinkedList
实现)。
总结
Java 数据结构的核心是集合框架,它把复杂的底层实现(数组、链表、哈希表、红黑树等)封装成简单的类,我们只需关注 “用什么结构” 和 “怎么用”。