在Java编程中,集合(Collection)是使用最频繁的组件之一。无论你是处理数据存储、检索还是操作,Java集合框架都提供了强大而灵活的工具集。本文将带你全面了解Java集合框架的各个组成部分,掌握如何根据场景选择最合适的集合类型。
一、集合框架概述
Java集合框架位于java.util包中,是一个用于表示和操作集合的统一架构。它的主要优势包括:
-
减少编程工作量:提供标准数据结构和算法
-
提高性能:优化过的实现
-
提高代码质量:类型安全且可复用的API
-
降低学习成本:一致的API设计
二、集合框架层次结构
核心接口关系
text
Iterable
↓
Collection Map
├── List ├── HashMap
├── Set ├── TreeMap
└── Queue ├── LinkedHashMap
└── ConcurrentHashMap
三、Collection接口详解
1. List接口 - 有序 可重复 集合
List允许重复元素,并且保持插入顺序。
ArrayList - 动态数组实现
java
// 创建与初始化
List<String> arrayList = new ArrayList<>();
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
// 常用操作
arrayList.add("Java");
arrayList.add(1, "Python"); // 在指定位置插入
String element = arrayList.get(0); // 随机访问 O(1)
arrayList.remove("Java");
// 遍历方式
// 1. for-each循环
for (String lang : arrayList) {
System.out.println(lang);
}
// 2. 迭代器
Iterator<String> iterator = arrayList.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 3. Java 8+ forEach
arrayList.forEach(System.out::println);
特点:
-
基于动态数组,随机访问快(O(1))
-
中间插入/删除慢(需要移动元素)
-
初始容量10,扩容1.5倍
-
非线程安全
LinkedList - 双向链表实现
java
List<String> linkedList = new LinkedList<>();
linkedList.addFirst("First"); // 头部添加 O(1)
linkedList.addLast("Last"); // 尾部添加 O(1)
linkedList.removeFirst(); // 头部移除 O(1)
适用场景:
-
频繁的插入删除操作
-
实现队列或双端队列
-
不需要随机访问
Vector vs ArrayList
-
Vector是线程安全的(方法同步),性能较低
-
ArrayList非线程安全,性能更好
-
现代开发中更多使用
Collections.synchronizedList()或并发集合
2. Set接口 - 不允许重复元素
HashSet - 基于HashMap
java
Set<String> hashSet = new HashSet<>();
hashSet.add("apple");
hashSet.add("apple"); // 重复元素,不会被添加
System.out.println(hashSet.contains("apple")); // true
// 自定义对象需要重写equals()和hashCode()
class Student {
private String id;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student student = (Student) o;
return Objects.equals(id, student.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
特点:
-
基于哈希表,插入、删除、查找接近O(1)
-
不保证顺序
-
允许一个null元素
LinkedHashSet - 保持插入顺序
java
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("first");
linkedHashSet.add("second");
// 遍历顺序与插入顺序一致
TreeSet - 排序集合
java
// 自然排序
Set<Integer> treeSet = new TreeSet<>();
treeSet.add(3);
treeSet.add(1);
treeSet.add(2);
// 遍历输出:1, 2, 3
// 自定义排序
Set<Student> sortedSet = new TreeSet<>(
Comparator.comparing(Student::getName)
.thenComparing(Student::getAge)
);
3. Queue接口 - 队列实现
PriorityQueue - 优先级队列
java
Queue<Integer> priorityQueue = new PriorityQueue<>();
priorityQueue.offer(5);
priorityQueue.offer(1);
priorityQueue.offer(3);
while (!priorityQueue.isEmpty()) {
System.out.println(priorityQueue.poll()); // 输出:1, 3, 5
}
ArrayDeque - 双端队列
java
Deque<String> deque = new ArrayDeque<>();
deque.offerFirst("first");
deque.offerLast("last");
deque.pollFirst();
deque.pollLast();
四、Map接口 - 键值对集合
HashMap - 最常用的Map实现
java
Map<String, Integer> hashMap = new HashMap<>();
// 添加元素
hashMap.put("Java", 1);
hashMap.put("Python", 2);
hashMap.putIfAbsent("Java", 3); // 已存在则不替换
// 获取元素
Integer value = hashMap.get("Java");
Integer defaultValue = hashMap.getOrDefault("C++", 0);
// 遍历方式
// 1. 遍历EntrySet
for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 2. Java 8+ forEach
hashMap.forEach((k, v) -> System.out.println(k + ": " + v));
// 3. 遍历键或值
for (String key : hashMap.keySet()) {
// 处理key
}
for (Integer val : hashMap.values()) {
// 处理value
}
Java 8+ HashMap优化:
-
链表长度超过8时转为红黑树
-
红黑树节点少于6时转回链表
-
优化哈希算法,减少碰撞
LinkedHashMap - 保持插入顺序
java
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("First", 1);
linkedHashMap.put("Second", 2);
// 遍历顺序与插入顺序一致
TreeMap - 排序键的Map
java
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Orange", 3);
treeMap.put("Apple", 1);
treeMap.put("Banana", 2);
// 按键的自然顺序排序:Apple, Banana, Orange
ConcurrentHashMap - 线程安全Map
java
ConcurrentMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("key", 1);
// 原子操作
concurrentMap.computeIfAbsent("key", k -> 1);
concurrentMap.computeIfPresent("key", (k, v) -> v + 1);
五、集合工具类 - Collections
java
List<Integer> list = Arrays.asList(3, 1, 4, 1, 5, 9);
// 排序
Collections.sort(list);
Collections.sort(list, Collections.reverseOrder());
// 查找
int index = Collections.binarySearch(list, 4);
// 同步包装(使非线程安全集合变为线程安全)
List<Integer> syncList = Collections.synchronizedList(new ArrayList<>());
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
// 不可变集合
List<Integer> unmodifiableList = Collections.unmodifiableList(list);
Set<Integer> singletonSet = Collections.singleton(1);
六、Java 8+ 新特性与集合
Stream API操作集合
java
List<String> languages = Arrays.asList("Java", "Python", "C++", "JavaScript");
// 过滤
List<String> filtered = languages.stream()
.filter(lang -> lang.length() > 3)
.collect(Collectors.toList());
// 映射
List<Integer> lengths = languages.stream()
.map(String::length)
.collect(Collectors.toList());
// 分组
Map<Integer, List<String>> groupByLength = languages.stream()
.collect(Collectors.groupingBy(String::length));
// 统计
IntSummaryStatistics stats = languages.stream()
.mapToInt(String::length)
.summaryStatistics();
System.out.println("平均长度: " + stats.getAverage());
Optional与集合
java
List<String> list = new ArrayList<>();
Optional<String> first = list.stream().findFirst();
first.ifPresent(System.out::println);
七、性能比较与选择
| 集合类型 | 获取 | 插入 | 删除 | 是否有序 | 线程安全 |
|---|---|---|---|---|---|
| ArrayList | O(1) | O(n) | O(n) | 插入顺序 | 否 |
| LinkedList | O(n) | O(1) | O(1) | 插入顺序 | 否 |
| HashSet | O(1) | O(1) | O(1) | 否 | 否 |
| TreeSet | O(log n) | O(log n) | O(log n) | 排序 | 否 |
| HashMap | O(1) | O(1) | O(1) | 否 | 否 |
| TreeMap | O(log n) | O(log n) | O(log n) | 键排序 | 否 |
选择建议:
-
需要快速随机访问 → ArrayList
-
频繁插入删除 → LinkedList
-
去重且不关心顺序 → HashSet
-
去重且需要排序 → TreeSet
-
键值对存储,快速查找 → HashMap
-
键值对,需要按键排序 → TreeMap
-
多线程环境 → ConcurrentHashMap, CopyOnWriteArrayList
八、最佳实践
1. 初始容量设置
java
// 预估元素数量,避免频繁扩容
List<String> list = new ArrayList<>(1000);
Map<String, Integer> map = new HashMap<>(1024);
2. 正确重写equals和hashCode
java
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyClass obj = (MyClass) o;
return Objects.equals(field1, obj.field1) &&
Objects.equals(field2, obj.field2);
}
@Override
public int hashCode() {
return Objects.hash(field1, field2);
}
3. 避免在遍历中修改集合
java
// 错误示例
for (String item : list) {
if (item.equals("remove")) {
list.remove(item); // 抛出ConcurrentModificationException
}
}
// 正确做法1:使用迭代器
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
if (iterator.next().equals("remove")) {
iterator.remove();
}
}
// 正确做法2:Java 8+ removeIf
list.removeIf(item -> item.equals("remove"));
4. 使用不可变集合
java
// Java 9+ 工厂方法
List<String> immutableList = List.of("A", "B", "C");
Set<Integer> immutableSet = Set.of(1, 2, 3);
Map<String, Integer> immutableMap = Map.of("A", 1, "B", 2);
九、总结
Java集合框架是Java开发者的核心工具包。掌握各种集合类型的特点、适用场景和性能特征,能够帮助我们编写出更高效、更健壮的代码。随着Java版本的更新,集合框架也在不断进化,引入了Stream API、不可变集合等现代特性,让集合操作更加简洁和安全。
记住,没有"最好"的集合,只有"最合适"的集合。根据具体需求选择合适的数据结构,是每个Java开发者必备的技能。
进一步学习建议:
-
深入阅读Java官方文档
-
研究开源项目中集合的使用
-
学习算法和数据结构理论
-
实践性能测试和对比
希望这篇博客能帮助你更好地理解和运用Java集合框架!如果你有任何问题或补充,欢迎在评论区留言讨论。