引言
Java集合框架是Java语言中用于存储和操作数据的一组核心接口和实现类。它提供了一套标准化的方法来处理各种数据结构,使开发人员能够专注于业务逻辑而不是底层数据结构的实现。本文将深入探讨Java集合框架的各个组成部分,并通过实例展示如何高效地使用它们。
一、集合框架概览
1.1 集合框架层次结构
Java集合框架主要分为两大分支:
-
Collection接口:单列数据集合,包括List、Set和Queue
-
Map接口:双列数据集合,存储键值对
java
// 集合框架主要接口关系图
// Collection (接口)
// ├── List (接口)
// │ ├── ArrayList (实现类)
// │ ├── LinkedList (实现类)
// │ └── Vector (实现类,线程安全)
// │ └── Stack (子类)
// ├── Set (接口)
// │ ├── HashSet (实现类)
// │ │ └── LinkedHashSet (子类)
// │ └── TreeSet (实现类)
// └── Queue (接口)
// ├── PriorityQueue (实现类)
// └── Deque (接口)
// └── ArrayDeque (实现类)
//
// Map (接口)
// ├── HashMap (实现类)
// │ └── LinkedHashMap (子类)
// ├── TreeMap (实现类)
// └── Hashtable (实现类,线程安全)
// └── Properties (子类)
1.2 集合框架的优势
-
标准化:统一的API设计,易于学习和使用
-
高性能:针对不同场景优化的实现
-
类型安全:通过泛型确保类型安全
-
可扩展:易于扩展和自定义实现
-
算法支持:内置排序、搜索等算法
二、Collection接口及其实现
2.1 List接口:有序、可重复的集合
java
import java.util.*;
public class ListExample {
public static void main(String[] args) {
// 1. ArrayList - 基于动态数组,随机访问快
List<String> arrayList = new ArrayList<>();
arrayList.add("Java");
arrayList.add("Python");
arrayList.add("C++");
arrayList.add(1, "JavaScript"); // 在指定位置插入
System.out.println("ArrayList内容: " + arrayList);
System.out.println("第二个元素: " + arrayList.get(1));
System.out.println("元素数量: " + arrayList.size());
// ArrayList的遍历方式
System.out.println("\nArrayList遍历:");
// 方式1:普通for循环
for (int i = 0; i < arrayList.size(); i++) {
System.out.print(arrayList.get(i) + " ");
}
// 方式2:增强for循环
System.out.println("\n增强for循环:");
for (String language : arrayList) {
System.out.print(language + " ");
}
// 方式3:迭代器
System.out.println("\n迭代器遍历:");
Iterator<String> iterator = arrayList.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
// 方式4:Lambda表达式(Java 8+)
System.out.println("\nLambda表达式遍历:");
arrayList.forEach(lang -> System.out.print(lang + " "));
// 方式5:方法引用
System.out.println("\n方法引用遍历:");
arrayList.forEach(System.out::print);
// 2. LinkedList - 基于双向链表,插入删除快
List<Integer> linkedList = new LinkedList<>();
linkedList.add(10);
linkedList.add(20);
linkedList.add(30);
linkedList.addFirst(5); // 在开头添加
linkedList.addLast(40); // 在末尾添加
System.out.println("\n\nLinkedList内容: " + linkedList);
System.out.println("第一个元素: " + ((LinkedList<Integer>) linkedList).getFirst());
System.out.println("最后一个元素: " + ((LinkedList<Integer>) linkedList).getLast());
// LinkedList作为队列使用
Queue<Integer> queue = new LinkedList<>(linkedList);
System.out.println("\n队列操作:");
System.out.println("队首元素: " + queue.peek());
System.out.println("出队元素: " + queue.poll());
System.out.println("剩余队列: " + queue);
// LinkedList作为栈使用
Deque<Integer> stack = new LinkedList<>();
stack.push(1); // 入栈
stack.push(2);
stack.push(3);
System.out.println("\n栈操作:");
System.out.println("栈顶元素: " + stack.peek());
System.out.println("出栈元素: " + stack.pop());
System.out.println("剩余栈: " + stack);
// 3. Vector - 线程安全的ArrayList(已过时,不推荐使用)
List<String> vector = new Vector<>();
vector.add("线程安全");
vector.add("但不推荐使用");
System.out.println("\nVector: " + vector);
}
}
2.2 Set接口:无序、不可重复的集合
java
import java.util.*;
public class SetExample {
public static void main(String[] args) {
// 1. HashSet - 基于哈希表,无序
Set<String> hashSet = new HashSet<>();
hashSet.add("Apple");
hashSet.add("Banana");
hashSet.add("Orange");
hashSet.add("Apple"); // 重复元素不会被添加
System.out.println("HashSet: " + hashSet);
System.out.println("包含Banana? " + hashSet.contains("Banana"));
System.out.println("Set大小: " + hashSet.size());
// 2. LinkedHashSet - 保持插入顺序的HashSet
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("第一");
linkedHashSet.add("第二");
linkedHashSet.add("第三");
linkedHashSet.add("第一"); // 不会重复添加
System.out.println("\nLinkedHashSet: " + linkedHashSet);
// 3. TreeSet - 基于红黑树,自动排序
Set<Integer> treeSet = new TreeSet<>();
treeSet.add(5);
treeSet.add(2);
treeSet.add(8);
treeSet.add(1);
treeSet.add(5); // 不会重复添加
System.out.println("\nTreeSet(自然排序): " + treeSet);
// 自定义排序的TreeSet
Set<String> customTreeSet = new TreeSet<>((s1, s2) ->
s2.compareTo(s1)); // 降序排序
customTreeSet.add("Java");
customTreeSet.add("Python");
customTreeSet.add("C++");
System.out.println("TreeSet(自定义降序): " + customTreeSet);
// Set操作:交集、并集、差集
Set<Integer> set1 = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
Set<Integer> set2 = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8));
// 并集
Set<Integer> union = new HashSet<>(set1);
union.addAll(set2);
System.out.println("\n并集: " + union);
// 交集
Set<Integer> intersection = new HashSet<>(set1);
intersection.retainAll(set2);
System.out.println("交集: " + intersection);
// 差集(set1 - set2)
Set<Integer> difference = new HashSet<>(set1);
difference.removeAll(set2);
System.out.println("差集(set1-set2): " + difference);
}
}
2.3 Queue接口:队列实现
java
import java.util.*;
public class QueueExample {
public static void main(String[] args) {
// 1. PriorityQueue - 优先级队列
Queue<Integer> priorityQueue = new PriorityQueue<>();
priorityQueue.offer(5);
priorityQueue.offer(1);
priorityQueue.offer(3);
priorityQueue.offer(2);
priorityQueue.offer(4);
System.out.println("PriorityQueue元素: " + priorityQueue);
System.out.println("按优先级出队:");
while (!priorityQueue.isEmpty()) {
System.out.print(priorityQueue.poll() + " ");
}
// 自定义优先级的PriorityQueue
Queue<String> customPriorityQueue = new PriorityQueue<>(
(s1, s2) -> s2.length() - s1.length() // 按字符串长度降序
);
customPriorityQueue.offer("Java");
customPriorityQueue.offer("Python");
customPriorityQueue.offer("C");
customPriorityQueue.offer("JavaScript");
System.out.println("\n\n按字符串长度优先级出队:");
while (!customPriorityQueue.isEmpty()) {
System.out.print(customPriorityQueue.poll() + " ");
}
// 2. ArrayDeque - 双端队列
Deque<String> deque = new ArrayDeque<>();
deque.addFirst("前端"); // 添加到队首
deque.addLast("后端"); // 添加到队尾
deque.offerFirst("移动端");
deque.offerLast("数据库");
System.out.println("\n\nArrayDeque内容: " + deque);
System.out.println("队首元素: " + deque.peekFirst());
System.out.println("队尾元素: " + deque.peekLast());
System.out.println("移除队首: " + deque.pollFirst());
System.out.println("移除队尾: " + deque.pollLast());
System.out.println("剩余队列: " + deque);
// 使用ArrayDeque作为栈
Deque<Integer> stack = new ArrayDeque<>();
stack.push(10); // 入栈
stack.push(20);
stack.push(30);
System.out.println("\nArrayDeque作为栈:");
System.out.println("栈顶元素: " + stack.peek());
System.out.println("出栈: " + stack.pop());
System.out.println("出栈后: " + stack);
}
}
三、Map接口及其实现
java
import java.util.*;
public class MapExample {
public static void main(String[] args) {
// 1. HashMap - 最常用的Map实现
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("Java", 95);
hashMap.put("Python", 90);
hashMap.put("C++", 88);
hashMap.put("Java", 96); // 更新已有键的值
hashMap.putIfAbsent("JavaScript", 92); // 仅当键不存在时放入
System.out.println("HashMap内容: " + hashMap);
System.out.println("Java的分数: " + hashMap.get("Java"));
System.out.println("包含Python键? " + hashMap.containsKey("Python"));
System.out.println("包含分数90? " + hashMap.containsValue(90));
System.out.println("Map大小: " + hashMap.size());
// HashMap遍历方式
System.out.println("\nHashMap遍历:");
// 方式1:遍历键集合
System.out.println("遍历键:");
for (String key : hashMap.keySet()) {
System.out.println(key + ": " + hashMap.get(key));
}
// 方式2:遍历值集合
System.out.println("\n遍历值:");
for (Integer value : hashMap.values()) {
System.out.println(value);
}
// 方式3:遍历键值对集合
System.out.println("\n遍历键值对:");
for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 方式4:迭代器遍历
System.out.println("\n迭代器遍历:");
Iterator<Map.Entry<String, Integer>> iterator =
hashMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> entry = iterator.next();
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 方式5:Lambda表达式(Java 8+)
System.out.println("\nLambda表达式遍历:");
hashMap.forEach((key, value) ->
System.out.println(key + ": " + value));
// 2. LinkedHashMap - 保持插入顺序的HashMap
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("第一", 1);
linkedHashMap.put("第二", 2);
linkedHashMap.put("第三", 3);
System.out.println("\nLinkedHashMap(保持插入顺序): " + linkedHashMap);
// 3. TreeMap - 基于红黑树,按键排序
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Orange", 3);
treeMap.put("Apple", 5);
treeMap.put("Banana", 2);
System.out.println("\nTreeMap(按键排序): " + treeMap);
// 自定义排序的TreeMap
Map<String, Integer> customTreeMap = new TreeMap<>(
(s1, s2) -> s2.compareTo(s1) // 降序排序
);
customTreeMap.put("Java", 95);
customTreeMap.put("Python", 90);
customTreeMap.put("C++", 88);
System.out.println("TreeMap(自定义降序): " + customTreeMap);
// Map操作示例
System.out.println("\nMap操作示例:");
// 获取或默认值
int javaScore = hashMap.getOrDefault("Java", 0);
int rubyScore = hashMap.getOrDefault("Ruby", 0);
System.out.println("Java分数: " + javaScore);
System.out.println("Ruby分数(默认0): " + rubyScore);
// 替换值
hashMap.replace("Python", 90, 95);
System.out.println("替换后Python分数: " + hashMap.get("Python"));
// 合并值
hashMap.merge("Java", 2, (oldValue, newValue) -> oldValue + newValue);
System.out.println("合并后Java分数: " + hashMap.get("Java"));
// 计算值(如果键不存在)
hashMap.computeIfAbsent("Go", k -> 85);
System.out.println("Go分数: " + hashMap.get("Go"));
// 删除键
hashMap.remove("C++");
System.out.println("删除C++后: " + hashMap);
}
}
四、集合工具类:Collections和Arrays
java
import java.util.*;
public class CollectionsExample {
public static void main(String[] args) {
// 创建测试数据
List<Integer> numbers = new ArrayList<>(Arrays.asList(5, 2, 8, 1, 9, 3));
List<String> fruits = new ArrayList<>(Arrays.asList(
"Apple", "Banana", "Orange", "Grape", "Mango"
));
// 1. 排序操作
System.out.println("原始列表: " + numbers);
Collections.sort(numbers);
System.out.println("排序后: " + numbers);
// 自定义排序
Collections.sort(fruits, (s1, s2) -> s2.compareTo(s1)); // 降序
System.out.println("水果降序: " + fruits);
// 2. 查找操作
int index = Collections.binarySearch(numbers, 8);
System.out.println("\n元素8的索引: " + index);
// 3. 反转和随机排序
Collections.reverse(numbers);
System.out.println("反转后: " + numbers);
Collections.shuffle(numbers);
System.out.println("随机排序后: " + numbers);
// 4. 最大最小值
System.out.println("最大值: " + Collections.max(numbers));
System.out.println("最小值: " + Collections.min(numbers));
// 5. 填充和复制
List<Integer> copy = new ArrayList<>(Collections.nCopies(5, 0));
System.out.println("\n填充5个0: " + copy);
Collections.fill(copy, 1);
System.out.println("填充为1后: " + copy);
// 6. 不可变集合
List<String> unmodifiableList =
Collections.unmodifiableList(fruits);
System.out.println("\n不可变列表: " + unmodifiableList);
// 7. 同步集合(线程安全)
List<String> synchronizedList =
Collections.synchronizedList(new ArrayList<>());
synchronizedList.add("线程安全");
System.out.println("同步列表: " + synchronizedList);
// 8. Arrays工具类使用
int[] array = {5, 2, 8, 1, 9};
System.out.println("\n原始数组: " + Arrays.toString(array));
Arrays.sort(array);
System.out.println("排序后数组: " + Arrays.toString(array));
int[] filledArray = new int[5];
Arrays.fill(filledArray, 7);
System.out.println("填充数组: " + Arrays.toString(filledArray));
int[] copyArray = Arrays.copyOf(array, 3);
System.out.println("复制前3个元素: " + Arrays.toString(copyArray));
}
}
五、Java 8+新特性:Stream API
java
import java.util.*;
import java.util.stream.*;
public class StreamExample {
public static void main(String[] args) {
List<String> languages = Arrays.asList(
"Java", "Python", "JavaScript", "C++", "Go", "Ruby", "Swift"
);
// 1. 过滤和收集
List<String> filtered = languages.stream()
.filter(lang -> lang.startsWith("J")) // 过滤以J开头的
.collect(Collectors.toList()); // 收集到List
System.out.println("以J开头的语言: " + filtered);
// 2. 映射转换
List<String> upperCase = languages.stream()
.map(String::toUpperCase) // 转为大写
.collect(Collectors.toList());
System.out.println("大写语言: " + upperCase);
// 3. 排序
List<String> sorted = languages.stream()
.sorted() // 自然排序
.collect(Collectors.toList());
System.out.println("排序后: " + sorted);
// 4. 限制和跳过
List<String> limited = languages.stream()
.limit(3) // 取前3个
.collect(Collectors.toList());
System.out.println("前3个语言: " + limited);
// 5. 统计操作
long count = languages.stream()
.filter(lang -> lang.length() > 3)
.count();
System.out.println("长度大于3的语言数量: " + count);
// 6. 查找操作
Optional<String> first = languages.stream()
.filter(lang -> lang.length() > 5)
.findFirst();
first.ifPresent(lang ->
System.out.println("第一个长度大于5的语言: " + lang));
// 7. 去重
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
List<Integer> distinct = numbers.stream()
.distinct() // 去重
.collect(Collectors.toList());
System.out.println("去重后: " + distinct);
// 8. 规约操作
Optional<Integer> sum = numbers.stream()
.reduce(Integer::sum); // 求和
sum.ifPresent(s -> System.out.println("总和: " + s));
// 9. 分组操作
Map<Integer, List<String>> grouped = languages.stream()
.collect(Collectors.groupingBy(String::length));
System.out.println("按长度分组: " + grouped);
// 10. 并行流
List<String> parallelResult = languages.parallelStream()
.filter(lang -> lang.length() > 2)
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());
System.out.println("并行流处理结果: " + parallelResult);
}
}
六、最佳实践和性能优化
6.1 集合选择指南
| 需求 | 推荐实现类 | 理由 |
|---|---|---|
| 快速随机访问 | ArrayList | 基于数组,O(1)访问 |
| 频繁插入删除 | LinkedList | 基于链表,O(1)插入删除 |
| 需要排序 | TreeSet/TreeMap | 自动排序 |
| 需要唯一元素 | HashSet/HashMap | 基于哈希表,O(1)查找 |
| 需要保持插入顺序 | LinkedHashSet/LinkedHashMap | 保持插入顺序 |
| 线程安全需求 | CopyOnWriteArrayList, ConcurrentHashMap | 并发安全 |
6.2 性能优化建议
java
import java.util.*;
public class PerformanceTips {
public static void main(String[] args) {
// 1. 指定初始容量
List<String> list = new ArrayList<>(1000); // 避免频繁扩容
Map<String, Integer> map = new HashMap<>(1000, 0.75f);
// 2. 使用增强for循环而非迭代器
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 推荐:增强for循环
for (Integer num : numbers) {
System.out.print(num + " ");
}
// 3. 使用entrySet遍历Map
Map<String, Integer> scores = new HashMap<>();
scores.put("Java", 95);
scores.put("Python", 90);
// 推荐:遍历entrySet
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 不推荐:遍历keySet再get
for (String key : scores.keySet()) {
System.out.println(key + ": " + scores.get(key)); // 两次查找
}
// 4. 批量操作
List<Integer> source = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> target = new ArrayList<>(source.size());
// 推荐:批量添加
target.addAll(source);
// 5. 使用不可变集合
List<String> immutableList = List.of("A", "B", "C"); // Java 9+
Set<String> immutableSet = Set.of("A", "B", "C");
Map<String, Integer> immutableMap = Map.of("A", 1, "B", 2);
// 6. 并发场景使用并发集合
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("Key1", 1);
concurrentMap.putIfAbsent("Key2", 2);
}
}
七、常见问题与解决方案
7.1 集合的线程安全问题
java
import java.util.*;
import java.util.concurrent.*;
public class ThreadSafeCollections {
public static void main(String[] args) throws InterruptedException {
// 1. 非线程安全的HashMap
Map<String, Integer> unsafeMap = new HashMap<>();
// 2. 使用Collections.synchronizedMap包装
Map<String, Integer> synchronizedMap =
Collections.synchronizedMap(new HashMap<>());
// 3. 使用ConcurrentHashMap(推荐)
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
// 测试线程安全
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
final int index = i;
executor.execute(() -> {
concurrentMap.put("key" + index, index);
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.SECONDS);
System.out.println("ConcurrentHashMap大小: " + concurrentMap.size());
// 4. CopyOnWriteArrayList用于读多写少的场景
List<String> copyOnWriteList = new CopyOnWriteArrayList<>();
copyOnWriteList.add("Java");
copyOnWriteList.add("Python");
// 迭代时安全,但写操作开销大
for (String item : copyOnWriteList) {
System.out.println(item);
}
}
}
7.2 避免ConcurrentModificationException
java
import java.util.*;
public class ConcurrentModificationFix {
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList(
"Java", "Python", "C++", "JavaScript"
));
// 错误示例:在迭代时修改集合
try {
for (String lang : list) {
if (lang.equals("C++")) {
list.remove(lang); // 抛出ConcurrentModificationException
}
}
} catch (ConcurrentModificationException e) {
System.out.println("错误:迭代时修改集合");
}
// 正确方法1:使用迭代器的remove方法
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String lang = iterator.next();
if (lang.equals("C++")) {
iterator.remove(); // 安全删除
}
}
System.out.println("方法1处理后: " + list);
// 正确方法2:使用Java 8+的removeIf
list.add("C++"); // 恢复数据
list.removeIf(lang -> lang.equals("C++"));
System.out.println("方法2处理后: " + list);
// 正确方法3:收集要删除的元素,最后统一删除
list.add("C++"); // 恢复数据
List<String> toRemove = new ArrayList<>();
for (String lang : list) {
if (lang.equals("C++")) {
toRemove.add(lang);
}
}
list.removeAll(toRemove);
System.out.println("方法3处理后: " + list);
}
}
总结
Java集合框架是Java编程中最重要的组成部分之一,掌握好集合框架的使用对于编写高效、可维护的代码至关重要。本文详细介绍了各种集合类的特点、使用场景和最佳实践:
-
根据需求选择合适的集合类
-
理解不同集合的性能特征
-
掌握集合的遍历和操作技巧
-
注意线程安全和并发修改问题
-
善用Java 8+的新特性如Stream API
记住,没有"最好"的集合,只有"最适合"的集合。在实际开发中,根据具体需求选择合适的集合实现,才能真正发挥集合框架的威力。
学习建议:
-
深入理解各种集合的内部实现原理
-
掌握Java 8+的Lambda表达式和Stream API
-
学习并发集合的使用场景
-
了解Java版本更新中集合框架的新特性
希望这篇博客能帮助您全面掌握Java集合框架,提升编程效率!