Java 集合框架是 Java 编程中处理数据集合的核心工具,掌握集合框架对于编写高效、健壮的 Java 程序至关重要。本文将从集合框架的体系结构出发,详细解析 List、Set、Map 三大接口及其实现类的特点、适用场景和常用操作,帮助读者全面掌握 Java 集合的使用技巧。
一、集合框架体系结构
Java 集合框架是一个精心设计的接口和类的层次结构,它提供了对数据集合的统一操作方式。整个框架的核心由三大接口构成:Collection、Map和Iterable,其中Collection和Map是最主要的两大接口体系。
1.1 集合框架层次图
1.2 核心接口与类说明
Collection接口:所有单列集合的根接口,定义了添加、删除、遍历等基本操作
List接口:有序(元素存入顺序和取出顺序一致)、可重复的集合
Set接口:无序、不可重复的集合
Map接口:键值对存储的集合,键唯一,值可重复
二、List 接口及实现类详解
List 是最常用的集合类型之一,其特点是元素有序且可重复,提供了按索引访问元素的能力。
2.1 ArrayList 详解
ArrayList 是基于数组实现的 List 接口,是最常用的 List 实现类。
核心特点:
底层使用 Object 数组存储元素
支持快速随机访问(时间复杂度 O (1))
插入和删除元素时需要移动后续元素,效率较低(时间复杂度 O (n))
非线程安全
常用操作示例:
// 创建ArrayList
List
// 添加元素
list.add("Java");
list.add("Python");
list.add("C++");
// 通过索引访问元素
System.out.println("第一个元素: " + list.get(0)); // 输出: 第一个元素: Java
// 修改元素
list.set(1, "Go");
// 删除元素
list.remove(2);
// 遍历方式1:for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// 遍历方式2:增强for循环
for (String item : list) {
System.out.println(item);
}
// 遍历方式3:Lambda表达式
list.forEach(item -> System.out.println(item));
内部实现原理:
ArrayList 内部维护了一个 Object 数组,初始容量为 10。当添加元素超过容量时,会进行扩容,新容量为原容量的 1.5 倍。扩容操作涉及数组复制,因此频繁扩容会影响性能。
2.2 LinkedList 详解
LinkedList 是基于双向链表实现的 List 接口,在插入和删除操作上表现更优。
核心特点:
底层使用双向链表结构
随机访问效率低(需要从头节点开始遍历,时间复杂度 O (n))
插入和删除操作效率高(只需修改节点引用,时间复杂度 O (1))
非线程安全
可以作为栈、队列或双端队列使用
常用操作示例:
// 创建LinkedList
List
// 添加元素
linkedList.add("A");
linkedList.addFirst("Z"); // 在头部添加元素
linkedList.addLast("B"); // 在尾部添加元素
// 作为队列使用
linkedList.offer("C"); // 添加元素到队尾
String first = linkedList.poll(); // 从队头取出并删除元素
// 作为栈使用
linkedList.push("D"); // 压栈
String top = linkedList.pop(); // 弹栈
// 遍历链表
Iterator
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
内部实现原理:
LinkedList 的每个节点包含前驱引用、后继引用和元素值。插入和删除操作只需修改相关节点的引用,无需移动大量元素,因此在频繁插入删除的场景下性能优于 ArrayList。
2.3 ArrayList vs LinkedList
特性ArrayListLinkedList数据结构数组双向链表随机访问高效 (O (1))低效 (O (n))插入 / 删除低效 (O (n))高效 (O (1))内存占用连续内存,占用较少分散内存,每个节点有额外引用,占用较多适用场景频繁查询,较少插入删除频繁插入删除,较少查询
三、Set 接口及实现类详解
Set 接口的特点是元素无序且不可重复,常用于需要去重或判断元素是否存在的场景。
3.1 HashSet 详解
HashSet 是基于哈希表实现的 Set 接口,是最常用的 Set 实现类。
核心特点:
底层使用 HashMap 实现(实际存储在 HashMap 的 key 中)
元素无序(存入和取出顺序可能不同)
不可重复
允许存储 null 元素(仅允许一个)
非线程安全
常用操作示例:
// 创建HashSet
Set
// 添加元素
set.add(10);
set.add(20);
set.add(10); // 重复元素,不会被添加
// 判断元素是否存在
System.out.println("包含10: " + set.contains(10)); // 输出: 包含10: true
// 删除元素
set.remove(20);
// 遍历Set
for (Integer item : set) {
System.out.println(item);
}
内部实现原理:
HashSet 内部使用 HashMap 存储元素,每个元素作为 HashMap 的 key,value 使用一个固定的 Object 对象。当添加元素时,通过 hashCode () 方法计算哈希值确定存储位置,并通过 equals () 方法判断是否重复。
3.2 TreeSet 详解
TreeSet 是基于红黑树实现的 Set 接口,元素会按照自然顺序或指定比较器排序。
核心特点:
底层使用 TreeMap 实现
元素有序(默认按自然顺序排序)
不可重复
不允许存储 null 元素
非线程安全
插入、删除、查询操作时间复杂度均为 O (log n)
// 创建TreeSet(自然排序)
Set
// 添加元素
treeSet.add("apple");
treeSet.add("banana");
treeSet.add("cherry");
// 遍历(有序)
for (String fruit : treeSet) {
System.out.println(fruit); // 按字母顺序输出
}
// 创建TreeSet(自定义排序)
Set
customSet.add(5);
customSet.add(3);
customSet.add(8);
// 获取极值
Integer first = customSet.first(); // 8
Integer last = customSet.last(); // 3
内部实现原理:
TreeSet 内部使用 TreeMap 存储元素,每个元素作为 TreeMap 的 key,value 使用固定的 Object 对象。TreeMap 基于红黑树实现,保证了元素的有序性和操作的高效性。
3.3 HashSet vs TreeSet
特性HashSetTreeSet数据结构哈希表红黑树元素顺序无序有序(自然或自定义顺序)插入效率高效 (O (1),不考虑哈希冲突)中等 (O (log n))查询效率高效 (O (1),不考虑哈希冲突)中等 (O (log n))适用场景需要去重,不关心顺序需要元素有序,或需要频繁获取极值、范围查询
四、Map 接口及实现类详解
Map 是键值对存储的集合,每个键唯一,值可以重复,是处理关联数据的重要工具。
4.1 HashMap 详解
HashMap 是基于哈希表实现的 Map 接口,是最常用的 Map 实现类。
核心特点:
底层使用数组 + 链表 + 红黑树实现(JDK1.8 及以后)
键唯一,值可重复
键可以为 null(仅允许一个),值可以为 null
非线程安全
插入、查询操作平均时间复杂度为 O (1)
常用操作示例:
// 创建HashMap
Map
// 添加键值对
map.put("Java", 100);
map.put("Python", 90);
map.put("C++", 85);
// 获取值
Integer score = map.get("Java"); // 100
// 修改值
map.put("Python", 95);
// 删除键值对
map.remove("C++");
// 判断键是否存在
boolean containsJava = map.containsKey("Java"); // true
// 遍历方式1:遍历键
for (String key : map.keySet()) {
System.out.println(key + ": " + map.get(key));
}
// 遍历方式2:遍历键值对
for (Map.Entry
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 遍历方式3:Lambda表达式
map.forEach((key, value) -> System.out.println(key + ": " + value));
内部实现原理:
HashMap 的底层数据结构在 JDK1.8 中发生了重要变化:
当链表长度超过阈值(默认 8)且数组容量超过 64 时,链表会转换为红黑树
插入元素时,先通过 hashCode () 计算哈希值,再通过扰动函数计算数组索引
当发生哈希冲突时,使用链表或红黑树存储冲突元素
4.2 TreeMap 详解
TreeMap 是基于红黑树实现的 Map 接口,键会按照自然顺序或指定比较器排序。
核心特点:
底层使用红黑树实现
键有序(默认按自然顺序排序)
键唯一,值可重复
键不允许为 null,值可以为 null
非线程安全
插入、删除、查询操作时间复杂度均为 O (log n)
常用操作示例:
// 创建TreeMap(自然排序)
Map
// 添加键值对
treeMap.put("apple", 10);
treeMap.put("banana", 20);
treeMap.put("cherry", 15);
// 遍历(按键的自然顺序)
for (Map.Entry
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 创建TreeMap(自定义排序)
Map
customMap.put(5, "five");
customMap.put(3, "three");
customMap.put(8, "eight");
// 获取极值
Integer firstKey = customMap.firstKey(); // 8
Integer lastKey = customMap.lastKey(); // 3
// 范围查询
Map
内部实现原理:
TreeMap 内部使用红黑树维护键的顺序,保证了键的有序性和操作的高效性。红黑树是一种自平衡二叉搜索树,通过颜色标记和旋转操作保持平衡,确保了 O (log n) 的操作复杂度。
4.3 HashMap vs TreeMap
特性HashMapTreeMap数据结构哈希表(数组 + 链表 + 红黑树)红黑树键顺序无序有序(自然或自定义顺序)插入效率高效 (O (1),不考虑哈希冲突)中等 (O (log n))查询效率高效 (O (1),不考虑哈希冲突)中等 (O (log n))适用场景需要快速查找、插入,不关心顺序需要键有序,或需要范围查询、获取极值
五、集合框架高级特性与最佳实践
5.1 泛型在集合中的应用
Java 泛型允许在定义集合时指定元素类型,提高代码的类型安全性和可读性。
// 无泛型
List list = new ArrayList();
list.add("Java");
list.add(100); // 允许添加不同类型元素
String str = (String) list.get(0); // 需要强制类型转换,可能引发ClassCastException
// 有泛型
List
genericList.add("Java");
// genericList.add(100); // 编译错误,类型不匹配
String safeStr = genericList.get(0); // 无需强制类型转换
5.2 集合的线程安全处理
Java 集合框架中的大部分实现类(如 ArrayList、HashMap)都是非线程安全的,在多线程环境下需要额外处理。
解决方案:
使用 Collections 工具类的同步包装器:
List
Map
使用 JUC 包中的并发集合:
// 并发List
List
// 并发Set
Set
// 并发Map
Map
5.3 集合性能优化最佳实践
合理选择集合类型:
需要频繁查询:选择 ArrayList、HashMap
需要频繁插入删除:选择 LinkedList、LinkedHashSet
需要元素有序:选择 TreeSet、TreeMap
需要线程安全:选择并发集合
预估集合容量:
// 预估容量,避免频繁扩容
List
避免不必要的装箱拆箱:
// 推荐使用包装类的缓存值
Integer a = 100; // 自动装箱,使用IntegerCache缓存
Integer b = 100;
System.out.println(a == b); // true
Integer c = 200;
Integer d = 200;
System.out.println(c == d); // false,超过缓存范围(-128~127)
使用增强 for 循环和 Lambda 表达式:
// 传统for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// 增强for循环
for (String item : list) {
System.out.println(item);
}
// Lambda表达式
list.forEach(System.out::println);
六、总结与学习建议
Java 集合框架是 Java 编程的核心基础,熟练掌握集合框架对于编写高效、健壮的 Java 程序至关重要。学习集合框架时,建议:
理解体系结构:先掌握集合框架的整体结构和核心接口,再深入学习具体实现类
对比学习:通过对比不同集合类的特点和适用场景,加深理解和记忆
动手实践:通过大量编码练习,掌握集合的常用操作和实现原理
阅读源码:有一定基础后,阅读集合类的源码,深入理解内部实现机制
您的支持是我持续创作的动力:
❤️ 点赞:如果这个项目对您有所启发,请多多点点赞支持一下
📌 收藏:完整项目源码已开源,有需要收藏自取
👀 关注:订阅我的专栏,不错过后续更多优质内容