一、Java集合框架概述
Java集合框架(Java Collections Framework)是Java语言中用于存储和操作数据集合的一组接口和类。它提供了一套标准化的工具来处理各种数据结构,如列表、集合、队列和映射等。
1.1 集合框架的层次结构
Java集合框架主要分为两大类:
-
Collection接口:表示一组对象
- List:有序可重复集合
- Set:无序不可重复集合
- Queue:队列,先进先出(FIFO)结构
-
Map接口:键值对映射
- 不是Collection的子接口,但属于集合框架的一部分
// 集合框架简单示例
List<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
Map<String, Integer> fruitPrices = new HashMap<>();
fruitPrices.put("Apple", 5);
fruitPrices.put("Banana", 3);
fruitPrices.put("Orange", 4);
1.2 集合框架的核心接口
接口 | 描述 | 主要实现类 |
---|---|---|
List | 有序集合(序列),允许重复元素 | ArrayList, LinkedList, Vector, Stack |
Set | 不包含重复元素的集合 | HashSet, LinkedHashSet, TreeSet |
Queue | 用于在处理前保存元素的集合,通常但不一定按FIFO原则排序 | LinkedList, PriorityQueue, ArrayDeque |
Deque | 双端队列,支持在两端插入和移除元素 | ArrayDeque, LinkedList |
Map | 键值对映射,键不可重复 | HashMap, LinkedHashMap, TreeMap |
SortedSet | 按升序维护其元素的Set | TreeSet |
SortedMap | 按升序维护其键的Map | TreeMap |
二、List接口及其实现类
2.1 ArrayList vs LinkedList
特性 | ArrayList | LinkedList |
---|---|---|
底层数据结构 | 动态数组 | 双向链表 |
随机访问性能 | O(1) - 快 | O(n) - 慢 |
插入/删除性能 | O(n) - 慢(除非在末尾) | O(1) - 快 |
内存占用 | 较少(仅存储数据) | 较多(存储数据和前后节点引用) |
适用场景 | 频繁随机访问 | 频繁插入删除 |
// ArrayList示例
List<String> arrayList = new ArrayList<>();
arrayList.add("Java");
arrayList.add("Python");
arrayList.add(1, "C++"); // 在索引1处插入
// LinkedList示例
List<String> linkedList = new LinkedList<>();
linkedList.add("Apple");
linkedList.addFirst("Banana"); // 在头部添加
linkedList.addLast("Orange"); // 在尾部添加
2.2 Vector vs ArrayList
特性 | Vector | ArrayList |
---|---|---|
同步性 | 线程安全 | 非线程安全 |
扩容策略 | 默认增长一倍 | 增长50% |
性能 | 较慢(同步开销) | 较快 |
迭代器 | fail-fast | fail-fast |
遗留类 | 是 | 否 |
// Vector示例 - 线程安全但性能较低
Vector<String> vector = new Vector<>();
vector.add("Red");
vector.add("Green");
vector.add("Blue");
// 多线程环境下安全
synchronized(vector) {
for (String color : vector) {
System.out.println(color);
}
}
2.3 List常用方法及示例
方法 | 描述 | 示例 |
---|---|---|
add(E e) | 向列表末尾添加元素 | list.add("Java"); |
add(int index, E element) | 在指定位置插入元素 | list.add(1, "Python"); |
get(int index) | 获取指定位置的元素 | String lang = list.get(0); |
set(int index, E element) | 替换指定位置的元素 | list.set(1, "C++"); |
remove(int index) | 移除指定位置的元素 | list.remove(0); |
remove(Object o) | 移除第一次出现的指定元素 | list.remove("Java"); |
indexOf(Object o) | 返回元素第一次出现的索引,不存在返回-1 | int index = list.indexOf("Python"); |
lastIndexOf(Object o) | 返回元素最后一次出现的索引,不存在返回-1 | int lastIndex = list.lastIndexOf("Java"); |
subList(int from, int to) | 返回子列表 | List<String> sub = list.subList(1, 3); |
sort(Comparator c) | 根据Comparator排序列表 | list.sort(Comparator.naturalOrder()); |
replaceAll(UnaryOperator u) | 对每个元素应用操作并替换 | list.replaceAll(String::toUpperCase); |
listIterator() | 返回列表迭代器,支持双向遍历 | ListIterator<String> it = list.listIterator(); |
// 综合示例:管理购物清单
List<String> shoppingList = new ArrayList<>();
shoppingList.add("牛奶");
shoppingList.add("面包");
shoppingList.add("鸡蛋");
// 在指定位置插入
shoppingList.add(1, "水果");
// 替换元素
shoppingList.set(2, "全麦面包");
// 移除元素
shoppingList.remove("鸡蛋");
// 排序
shoppingList.sort(String::compareToIgnoreCase);
// 使用Java 8 forEach遍历
shoppingList.forEach(item -> System.out.println("需要购买: " + item));
// 使用流过滤
List<String> filtered = shoppingList.stream()
.filter(item -> item.length() > 2)
.collect(Collectors.toList());
三、Set接口及其实现类
3.1 HashSet vs LinkedHashSet vs TreeSet
特性 | HashSet | LinkedHashSet | TreeSet |
---|---|---|---|
底层实现 | HashMap | LinkedHashMap | TreeMap |
排序 | 无序 | 插入顺序 | 自然顺序或Comparator指定顺序 |
性能(添加/删除/查找) | O(1) | O(1) | O(log n) |
允许null | 是 | 是 | 否(如果使用自然排序) |
线程安全 | 否 | 否 | 否 |
适用场景 | 需要快速查找且不关心顺序 | 需要保持插入顺序 | 需要排序 |
// HashSet示例 - 无序但快速查找
Set<String> hashSet = new HashSet<>();
hashSet.add("Java");
hashSet.add("Python");
hashSet.add("C++");
hashSet.add("Java"); // 重复元素不会被添加
// LinkedHashSet示例 - 保持插入顺序
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("Java");
linkedHashSet.add("Python");
linkedHashSet.add("C++");
// TreeSet示例 - 自动排序
Set<String> treeSet = new TreeSet<>();
treeSet.add("Java");
treeSet.add("Python");
treeSet.add("C++");
3.2 Set常用方法及示例
方法 | 描述 | 示例 |
---|---|---|
add(E e) | 添加元素(如果不存在) | set.add("Java"); |
remove(Object o) | 移除指定元素 | set.remove("Java"); |
contains(Object o) | 检查是否包含指定元素 | boolean hasJava = set.contains("Java"); |
size() | 返回集合大小 | int size = set.size(); |
isEmpty() | 检查集合是否为空 | boolean empty = set.isEmpty(); |
clear() | 清空集合 | set.clear(); |
iterator() | 返回迭代器 | Iterator<String> it = set.iterator(); |
addAll(Collection c) | 添加集合中所有元素 | set.addAll(otherSet); |
retainAll(Collection c) | 仅保留指定集合中的元素(交集) | set.retainAll(otherSet); |
removeAll(Collection c) | 移除指定集合中的所有元素(差集) | set.removeAll(otherSet); |
toArray() | 转换为数组 | Object[] array = set.toArray(); |
stream() | 返回流(Java 8) | set.stream().filter(s -> s.startsWith("J")).forEach(System.out::println); |
// 综合示例:管理课程集合
Set<String> courseSet = new HashSet<>();
courseSet.add("数学");
courseSet.add("英语");
courseSet.add("物理");
// 检查元素是否存在
if (courseSet.contains("数学")) {
System.out.println("已选修数学");
}
// 使用迭代器遍历
Iterator<String> iterator = courseSet.iterator();
while (iterator.hasNext()) {
System.out.println("课程: " + iterator.next());
}
// Java 8 流操作
long count = courseSet.stream()
.filter(course -> course.length() > 2)
.count();
// 集合运算示例
Set<String> scienceCourses = Set.of("物理", "化学", "生物");
// 并集
Set<String> allCourses = new HashSet<>(courseSet);
allCourses.addAll(scienceCourses);
// 交集
Set<String> commonCourses = new HashSet<>(courseSet);
commonCourses.retainAll(scienceCourses);
// 差集
Set<String> diffCourses = new HashSet<>(courseSet);
diffCourses.removeAll(scienceCourses);
四、Map接口及其实现类
4.1 HashMap vs LinkedHashMap vs TreeMap
特性 | HashMap | LinkedHashMap | TreeMap |
---|---|---|---|
底层实现 | 数组+链表/红黑树(JDK8+) | HashMap+双向链表 | 红黑树 |
排序 | 无序 | 插入顺序或访问顺序 | 键的自然顺序或Comparator指定顺序 |
性能(添加/删除/查找) | O(1) | O(1) | O(log n) |
允许null键/值 | 是/是 | 是/是 | 否/是(取决于Comparator) |
线程安全 | 否 | 否 | 否 |
内存消耗 | 较低 | 较高(维护链表) | 较高(树结构) |
// HashMap示例 - 快速键值查找
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("Java", 1);
hashMap.put("Python", 2);
hashMap.put("C++", 3);
// LinkedHashMap示例 - 保持插入顺序
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("Java", 1);
linkedHashMap.put("Python", 2);
linkedHashMap.put("C++", 3);
// 按访问顺序排序的LinkedHashMap(最近最少使用)
Map<String, Integer> accessOrderMap = new LinkedHashMap<>(16, 0.75f, true);
accessOrderMap.put("Java", 1);
accessOrderMap.put("Python", 2);
accessOrderMap.put("C++", 3);
accessOrderMap.get("Java"); // 访问后Java变为最后一项
// TreeMap示例 - 按键排序
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Java", 1);
treeMap.put("Python", 2);
treeMap.put("C++", 3);
4.2 Map常用方法及示例
方法 | 描述 | 示例 |
---|---|---|
put(K key, V value) | 添加键值对 | map.put("Java", 1); |
get(Object key) | 获取指定键的值 | Integer value = map.get("Java"); |
remove(Object key) | 移除指定键的映射 | map.remove("Java"); |
containsKey(Object key) | 检查是否包含指定键 | boolean hasJava = map.containsKey("Java"); |
containsValue(Object value) | 检查是否包含指定值 | boolean hasValue1 = map.containsValue(1); |
size() | 返回映射数量 | int size = map.size(); |
isEmpty() | 检查映射是否为空 | boolean empty = map.isEmpty(); |
clear() | 清空映射 | map.clear(); |
keySet() | 返回所有键的Set视图 | Set<String> keys = map.keySet(); |
values() | 返回所有值的Collection视图 | Collection<Integer> values = map.values(); |
entrySet() | 返回所有键值对的Set视图 | Set<Map.Entry<String, Integer>> entries = map.entrySet(); |
putAll(Map m) | 添加所有映射 | map.putAll(otherMap); |
getOrDefault(K key, V defaultValue) | 获取值或默认值(Java 8) | int val = map.getOrDefault("Ruby", 0); |
putIfAbsent(K key, V value) | 仅当键不存在时添加(Java 8) | map.putIfAbsent("Java", 10); |
merge(K key, V value, BiFunction) | 合并操作(Java 8) | map.merge("Java", 1, Integer::sum); |
compute(K key, BiFunction) | 计算新值(Java 8) | map.compute("Java", (k, v) -> v == null ? 1 : v + 1); |
// 综合示例:学生成绩管理
Map<String, Integer> studentScores = new HashMap<>();
studentScores.put("张三", 85);
studentScores.put("李四", 92);
studentScores.put("王五", 78);
// 检查键是否存在
if (studentScores.containsKey("张三")) {
System.out.println("张三的成绩已记录");
}
// 遍历键值对(Java 8之前)
for (Map.Entry<String, Integer> entry : studentScores.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// Java 8 forEach遍历
studentScores.forEach((name, score) ->
System.out.println(name + "的成绩是: " + score)
);
// 使用流处理
double average = studentScores.values().stream()
.mapToInt(Integer::intValue)
.average()
.orElse(0.0);
// 合并操作示例
Map<String, Integer> additionalScores = new HashMap<>();
additionalScores.put("张三", 5); // 加分
additionalScores.put("赵六", 88); // 新学生
additionalScores.forEach((name, score) ->
studentScores.merge(name, score, Integer::sum)
);
// 计算操作示例
studentScores.computeIfAbsent("钱七", k -> 90); // 如果不存在则添加
studentScores.computeIfPresent("王五", (k, v) -> v + 2); // 如果存在则加分
五、Queue和Deque接口
5.1 Queue实现类比较
特性 | LinkedList | PriorityQueue | ArrayDeque |
---|---|---|---|
底层实现 | 双向链表 | 堆(优先队列) | 循环数组 |
排序 | FIFO | 自然顺序或Comparator指定顺序 | FIFO或LIFO |
性能(添加/删除) | O(1) | O(log n)插入,O(1)获取 | O(1) |
允许null | 是 | 否 | 否 |
线程安全 | 否 | 否 | 否 |
适用场景 | 通用队列 | 需要优先级处理的元素 | 高效实现栈和队列 |
// Queue示例 - 先进先出
Queue<String> queue = new LinkedList<>();
queue.offer("第一人"); // 入队
queue.offer("第二人");
queue.offer("第三人");
String first = queue.poll(); // 出队 - "第一人"
// PriorityQueue示例 - 优先级队列
Queue<Integer> priorityQueue = new PriorityQueue<>();
priorityQueue.offer(5);
priorityQueue.offer(1);
priorityQueue.offer(3);
int firstNum = priorityQueue.poll(); // 1 (最小优先)
// 自定义优先级
Queue<String> customPriority = new PriorityQueue<>(
(a, b) -> b.length() - a.length() // 按长度降序
);
customPriority.offer("short");
customPriority.offer("very long");
customPriority.offer("medium");
String longest = customPriority.poll(); // "very long"
5.2 Deque实现类比较
特性 | LinkedList | ArrayDeque |
---|---|---|
底层实现 | 双向链表 | 循环数组 |
性能(添加/删除) | O(1) | O(1) |
内存占用 | 较高(节点开销) | 较低 |
随机访问 | O(n) | O(1) |
适用场景 | 需要同时作为List和Deque使用 | 纯Deque操作,性能要求高 |
// Deque示例 - 双端队列
Deque<String> deque = new ArrayDeque<>();
deque.offerFirst("第一"); // 头部添加
deque.offerLast("最后"); // 尾部添加
deque.offer("默认尾部"); // 同offerLast
String first = deque.pollFirst(); // 头部移除
String last = deque.pollLast(); // 尾部移除
// 作为栈使用
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1); // 压栈
stack.push(2);
stack.push(3);
int top = stack.pop(); // 弹栈 - 3
5.3 Queue/Deque常用方法
方法 | Queue描述 | Deque描述(额外方法) |
---|---|---|
add(e)/offer(e) | 添加元素到队尾 | addLast(e)/offerLast(e) |
remove()/poll() | 移除并返回队头元素 | removeFirst()/pollFirst() |
element()/peek() | 获取但不移除队头元素 | getFirst()/peekFirst() |
- | - | addFirst(e)/offerFirst(e) |
- | - | removeLast()/pollLast() |
- | - | getLast()/peekLast() |
push(e) | - | 压栈(同addFirst) |
pop() | - | 弹栈(同removeFirst) |
// 综合示例:银行排队系统
Queue<String> bankQueue = new LinkedList<>();
bankQueue.offer("客户A");
bankQueue.offer("客户B");
bankQueue.offer("VIP客户");
// 处理队列
while (!bankQueue.isEmpty()) {
String customer = bankQueue.poll();
System.out.println("正在处理: " + customer);
}
// 使用Deque实现撤销功能
Deque<String> actionHistory = new ArrayDeque<>();
actionHistory.push("添加文本");
actionHistory.push("格式化文档");
actionHistory.push("插入图片");
// 撤销操作
String lastAction = actionHistory.pop();
System.out.println("撤销: " + lastAction);
// 查看但不撤销
String nextAction = actionHistory.peek();
System.out.println("下一个可撤销操作: " + nextAction);
六、Java 8新特性与集合
6.1 Lambda表达式与集合
Java 8引入的Lambda表达式极大简化了集合操作:
List<String> languages = Arrays.asList("Java", "Python", "C++", "JavaScript");
// 传统匿名类方式
Collections.sort(languages, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
});
// Lambda表达式方式
Collections.sort(languages, (a, b) -> a.length() - b.length());
// 方法引用方式
languages.sort(Comparator.comparingInt(String::length));
// 遍历集合
languages.forEach(System.out::println);
6.2 Stream API
Stream API提供了一种高效处理集合数据的函数式编程方式:
List<Employee> employees = Arrays.asList(
new Employee("张三", "研发部", 8000),
new Employee("李四", "市场部", 6000),
new Employee("王五", "研发部", 9000),
new Employee("赵六", "销售部", 7000)
);
// 过滤与收集
List<Employee> devTeam = employees.stream()
.filter(e -> "研发部".equals(e.getDepartment()))
.collect(Collectors.toList());
// 分组
Map<String, List<Employee>> byDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
// 计算平均工资
double avgSalary = employees.stream()
.mapToInt(Employee::getSalary)
.average()
.orElse(0);
// 排序与限制
List<Employee> topEarners = employees.stream()
.sorted(Comparator.comparingInt(Employee::getSalary).reversed())
.limit(2)
.collect(Collectors.toList());
6.3 新的Map方法
Java 8为Map接口添加了许多实用方法:
Map<String, Integer> wordCounts = new HashMap<>();
wordCounts.put("Java", 1);
wordCounts.put("Python", 3);
// 如果不存在则计算
wordCounts.computeIfAbsent("C++", k -> 0);
// 如果存在则更新
wordCounts.computeIfPresent("Java", (k, v) -> v + 1);
// 合并值
wordCounts.merge("Python", 1, Integer::sum);
// 遍历
wordCounts.forEach((word, count) ->
System.out.println(word + ": " + count)
);
// 获取或默认值
int rubyCount = wordCounts.getOrDefault("Ruby", 0);
6.4 Collectors工具类
Collectors提供了丰富的收集器实现:
List<Employee> employees = // ... 同上
// 转换为Map
Map<String, Integer> nameToSalary = employees.stream()
.collect(Collectors.toMap(
Employee::getName,
Employee::getSalary));
// 分组并计算统计量
Map<String, Double> avgSalaryByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.averagingInt(Employee::getSalary)));
// 分区(按条件分为两部分)
Map<Boolean, List<Employee>> partitioned = employees.stream()
.collect(Collectors.partitioningBy(
e -> e.getSalary() > 7000));
// 连接字符串
String allNames = employees.stream()
.map(Employee::getName)
.collect(Collectors.joining(", "));
// 汇总统计
IntSummaryStatistics stats = employees.stream()
.collect(Collectors.summarizingInt(Employee::getSalary));
System.out.println("平均工资: " + stats.getAverage());
System.out.println("最高工资: " + stats.getMax());
七、集合的线程安全与并发
7.1 传统线程安全集合
类 | 描述 | 替代方案(Java 5+) |
---|---|---|
Vector | 线程安全的List实现 | Collections.synchronizedList |
Hashtable | 线程安全的Map实现 | Collections.synchronizedMap |
Stack | 线程安全的栈实现 | ArrayDeque |
// 同步包装示例
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
// 使用同步集合
synchronized(syncList) {
syncList.add("Java");
}
// 遍历时需要同步
synchronized(syncMap) {
for (Map.Entry<String, Integer> entry : syncMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
7.2 java.util.concurrent包中的集合
Java 5+引入了更高效的并发集合:
类 | 描述 | 特点 |
---|---|---|
ConcurrentHashMap | 高并发Map实现 | 分段锁,高并发下性能好 |
CopyOnWriteArrayList | 写时复制List | 读多写少场景性能好 |
ConcurrentLinkedQueue | 无界线程安全队列 | 非阻塞算法 |
BlockingQueue | 阻塞队列接口 | 支持生产者-消费者模式 |
ConcurrentSkipListMap | 并发版TreeMap | 跳表实现,有序 |
ConcurrentSkipListSet | 并发版TreeSet | 跳表实现,有序 |
// ConcurrentHashMap示例
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("Java", 1);
concurrentMap.putIfAbsent("Python", 2);
// 原子操作
concurrentMap.compute("Java", (k, v) -> v == null ? 1 : v + 1);
// 遍历不需要同步
concurrentMap.forEach((k, v) -> System.out.println(k + ": " + v));
// CopyOnWriteArrayList示例 - 适合监听器列表等读多写少场景
CopyOnWriteArrayList<String> listeners = new CopyOnWriteArrayList<>();
listeners.add("Listener1");
listeners.add("Listener2");
// 遍历时安全,但可能看到旧数据
listeners.forEach(listener -> System.out.println("通知: " + listener));
// BlockingQueue示例 - 生产者消费者模式
BlockingQueue<String> queue = new LinkedBlockingQueue<>(10);
// 生产者线程
new Thread(() -> {
try {
queue.put("任务1");
queue.put("任务2");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// 消费者线程
new Thread(() -> {
try {
String task = queue.take();
System.out.println("处理: " + task);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
7.3 并发集合性能比较
操作 | HashMap | ConcurrentHashMap | Hashtable | Collections.synchronizedMap |
---|---|---|---|---|
读(高并发) | 不安全 | 非常好 | 差 | 差 |
写(高并发) | 不安全 | 非常好 | 差 | 差 |
单线程读 | 最好 | 稍慢 | 慢 | 慢 |
单线程写 | 最好 | 稍慢 | 慢 | 慢 |
迭代时修改 | 抛异常 | 安全但不一致 | 抛异常 | 抛异常 |
选择建议:
- 单线程环境:使用普通集合
- 多线程读多写少:
CopyOnWriteArrayList
/CopyOnWriteArraySet
- 多线程读写频繁:
ConcurrentHashMap
/ConcurrentSkipListMap
- 需要阻塞操作:
BlockingQueue
实现
八、集合的最佳实践与性能优化
8.1 集合选择指南
选择List实现:
- 需要快速随机访问:
ArrayList
- 频繁在任意位置插入删除:
LinkedList
- 多线程环境:
CopyOnWriteArrayList
(读多写少)或同步包装
选择Set实现:
- 一般用途:
HashSet
- 需要保持插入顺序:
LinkedHashSet
- 需要排序:
TreeSet
- 高并发:
ConcurrentSkipListSet
或CopyOnWriteArraySet
选择Map实现:
- 一般用途:
HashMap
- 需要保持插入顺序:
LinkedHashMap
- 需要排序:
TreeMap
- 高并发:
ConcurrentHashMap
或ConcurrentSkipListMap
选择Queue实现:
- 一般队列:
LinkedList
或ArrayDeque
- 优先级队列:
PriorityQueue
- 线程安全队列:
ConcurrentLinkedQueue
- 阻塞队列:
ArrayBlockingQueue
/LinkedBlockingQueue
8.2 性能优化技巧
-
初始化合适容量:
// 避免频繁扩容 List<String> list = new ArrayList<>(1000); Map<String, Integer> map = new HashMap<>(1024, 0.75f);
-
使用批量操作:
// 避免多次调用add List<String> targetList = new ArrayList<>(); targetList.addAll(sourceList); // 使用addAll或构造方法 Set<String> set = new HashSet<>(anotherCollection);
-
避免不必要的装箱:
// 使用原始类型专用集合 IntList intList = new IntArrayList(); // (第三方库如Eclipse Collections)
-
选择合适的数据结构:
// 频繁检查存在性使用Set而不是List Set<String> uniqueNames = new HashSet<>(nameList);
-
利用视图和子集合:
// 不复制数据的情况下操作部分集合 List<String> subList = largeList.subList(100, 200); subList.clear(); // 清除原集合的部分元素
-
并行流谨慎使用:
// 只有大数据量且无状态操作时才使用并行流 List<String> result = largeList.parallelStream() .filter(s -> s.length() > 5) .collect(Collectors.toList());
8.3 常见陷阱与避免方法
-
并发修改异常:
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c")); // 错误方式 - 会抛出ConcurrentModificationException for (String s : list) { if ("b".equals(s)) { list.remove(s); } } // 正确方式1 - 使用迭代器 Iterator<String> it = list.iterator(); while (it.hasNext()) { if ("b".equals(it.next())) { it.remove(); } } // 正确方式2 - Java 8 removeIf list.removeIf(s -> "b".equals(s));
-
可变对象作为Map键:
class Employee { String name; // equals和hashCode基于name } Employee emp = new Employee("张三"); Map<Employee, String> map = new HashMap<>(); map.put(emp, "开发"); emp.name = "李四"; // 修改键对象 System.out.println(map.get(emp)); // 可能返回null
-
未实现equals/hashCode:
class BadKey { int id; // 没有重写equals和hashCode } Map<BadKey, String> map = new HashMap<>(); BadKey key1 = new BadKey(); key1.id = 1; map.put(key1, "值1"); BadKey key2 = new BadKey(); key2.id = 1; System.out.println(map.get(key2)); // 返回null
-
原始类型集合:
// 自动装箱性能开销 List<Integer> list = new ArrayList<>(); for (int i = 0; i < 1000000; i++) { list.add(i); // 发生自动装箱 } // 考虑使用第三方原始类型集合 IntList intList = new IntArrayList(); // Eclipse Collections
-
集合与数组转换:
List<String> list = Arrays.asList("a", "b", "c"); // 错误方式 - 返回的数组是固定大小的 String[] array = list.toArray(new String[0]); array[0] = "d"; // 修改会影响原List // 正确方式 - 创建新数组 String[] newArray = new String[list.size()]; list.toArray(newArray);
九、实战案例:电商系统集合应用
9.1 商品库存管理
// 商品类
class Product {
private String id;
private String name;
private double price;
private int stock;
// 构造方法、getter/setter省略
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Product product = (Product) o;
return id.equals(product.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
// 库存管理
public class InventoryManager {
private Map<String, Product> productMap = new HashMap<>();
private Set<Product> lowStockProducts = new TreeSet<>(
Comparator.comparingInt(Product::getStock)
);
// 添加商品
public void addProduct(Product product) {
productMap.put(product.getId(), product);
checkLowStock(product);
}
// 更新库存
public void updateStock(String productId, int quantity) {
Product product = productMap.get(productId);
if (product != null) {
lowStockProducts.remove(product); // 先移除
product.setStock(product.getStock() + quantity);
checkLowStock(product); // 重新检查
}
}
// 检查低库存
private void checkLowStock(Product product) {
if (product.getStock() < 10) {
lowStockProducts.add(product);
}
}
// 获取低库存商品
public List<Product> getLowStockProducts() {
return new ArrayList<>(lowStockProducts);
}
// 根据价格范围筛选商品
public List<Product> getProductsInPriceRange(double min, double max) {
return productMap.values().stream()
.filter(p -> p.getPrice() >= min && p.getPrice() <= max)
.sorted(Comparator.comparingDouble(Product::getPrice))
.collect(Collectors.toList());
}
}
9.2 订单处理系统
class Order {
private String orderId;
private String customerId;
private LocalDateTime orderDate;
private List<OrderItem> items;
private OrderStatus status;
// 构造方法、getter/setter省略
public double getTotalAmount() {
return items.stream()
.mapToDouble(item -> item.getPrice() * item.getQuantity())
.sum();
}
}
enum OrderStatus { PENDING, PROCESSING, SHIPPED, DELIVERED, CANCELLED }
class OrderItem {
private String productId;
private String productName;
private double price;
private int quantity;
// 构造方法、getter/setter省略
}
public class OrderProcessor {
private Queue<Order> pendingOrders = new ConcurrentLinkedQueue<>();
private Map<String, Order> orderHistory = new ConcurrentHashMap<>();
private Map<String, List<Order>> customerOrders = new ConcurrentHashMap<>();
// 提交订单
public void submitOrder(Order order) {
order.setStatus(OrderStatus.PENDING);
pendingOrders.offer(order);
orderHistory.put(order.getOrderId(), order);
customerOrders.computeIfAbsent(order.getCustomerId(), k -> new ArrayList<>())
.add(order);
}
// 处理订单
public void processOrders() {
while (!pendingOrders.isEmpty()) {
Order order = pendingOrders.poll();
if (order != null) {
processOrder(order);
}
}
}
private void processOrder(Order order) {
order.setStatus(OrderStatus.PROCESSING);
// 模拟处理逻辑
try {
Thread.sleep(100); // 模拟处理时间
order.setStatus(OrderStatus.SHIPPED);
} catch (InterruptedException e) {
order.setStatus(OrderStatus.CANCELLED);
Thread.currentThread().interrupt();
}
}
// 获取客户订单历史
public List<Order> getCustomerOrders(String customerId) {
return Collections.unmodifiableList(
customerOrders.getOrDefault(customerId, Collections.emptyList())
);
}
// 统计销售数据
public Map<String, Double> getSalesByProduct() {
return orderHistory.values().stream()
.flatMap(order -> order.getItems().stream())
.collect(Collectors.groupingBy(
OrderItem::getProductName,
Collectors.summingDouble(item -> item.getPrice() * item.getQuantity())
));
}
}
9.3 用户购物车实现
public class ShoppingCart {
private Map<String, CartItem> items = new LinkedHashMap<>(); // 保持添加顺序
private List<CartObserver> observers = new CopyOnWriteArrayList<>();
// 添加商品
public void addItem(Product product, int quantity) {
items.compute(product.getId(), (id, existingItem) -> {
if (existingItem == null) {
return new CartItem(product, quantity);
}
existingItem.increaseQuantity(quantity);
return existingItem;
});
notifyObservers();
}
// 移除商品
public void removeItem(String productId) {
CartItem removed = items.remove(productId);
if (removed != null) {
notifyObservers();
}
}
// 更新数量
public void updateQuantity(String productId, int newQuantity) {
if (newQuantity <= 0) {
removeItem(productId);
return;
}
CartItem item = items.get(productId);
if (item != null) {
item.setQuantity(newQuantity);
notifyObservers();
}
}
// 计算总价
public double getTotalPrice() {
return items.values().stream()
.mapToDouble(CartItem::getTotalPrice)
.sum();
}
// 获取所有商品
public List<CartItem> getAllItems() {
return new ArrayList<>(items.values());
}
// 清空购物车
public void clear() {
items.clear();
notifyObservers();
}
// 观察者模式
public void addObserver(CartObserver observer) {
observers.add(observer);
}
public void removeObserver(CartObserver observer) {
observers.remove(observer);
}
private void notifyObservers() {
observers.forEach(observer -> observer.cartUpdated(this));
}
}
class CartItem {
private Product product;
private int quantity;
// 构造方法、getter/setter省略
public double getTotalPrice() {
return product.getPrice() * quantity;
}
public void increaseQuantity(int amount) {
quantity += amount;
}
}
interface CartObserver {
void cartUpdated(ShoppingCart cart);
}
十、总结与扩展
10.1 集合框架核心要点总结
-
List:有序可重复集合,关注索引和顺序
ArrayList
:随机访问快,插入删除慢LinkedList
:插入删除快,随机访问慢Vector
:线程安全但性能低
-
Set:无序不重复集合,关注唯一性
HashSet
:快速查找,无序LinkedHashSet
:保持插入顺序TreeSet
:自动排序
-
Map:键值对映射
HashMap
:通用键值存储LinkedHashMap
:保持插入顺序TreeMap
:按键排序ConcurrentHashMap
:高并发场景
-
Queue/Deque:队列和双端队列
LinkedList
:通用实现ArrayDeque
:高效数组实现PriorityQueue
:优先级队列
-
Java 8增强:
- Lambda表达式简化集合操作
- Stream API提供函数式数据处理
- 新增Map方法(
compute
,merge
等)
10.2 第三方集合库
-
Guava (Google Core Libraries for Java):
- 不可变集合:
ImmutableList
,ImmutableSet
,ImmutableMap
- 多值Map:
Multimap
- 双向Map:
BiMap
- 表格结构:
Table
- 不可变集合:
-
Eclipse Collections:
- 原始类型集合:
IntList
,LongSet
等 - 丰富的容器类型和算法
- 原始类型集合:
-
Apache Commons Collections:
- 扩展的集合接口和实现
- 各种实用工具类
10.3 未来发展趋势
-
Valhalla项目:
- 值类型和专用泛型
- 可能减少集合的装箱开销
-
更丰富的流操作:
- Java 9+增加了更多Stream操作
- 响应式流集成
-
记录类(Record)与集合:
- Java 16引入的记录类简化了值对象
- 与集合框架更好集成
// Java 16记录类示例
record ProductRecord(String id, String name, double price) {}
List<ProductRecord> products = List.of(
new ProductRecord("p1", "Laptop", 999.99),
new ProductRecord("p2", "Phone", 699.99)
);
// 自动实现equals/hashCode/toString等
Map<String, ProductRecord> productMap = products.stream()
.collect(Collectors.toMap(ProductRecord::id, Function.identity()));
通过这篇全面的指南,你应该已经掌握了Java集合框架从基础到高级的所有核心概念。记住,选择正确的集合类型和实现类对于编写高效、可维护的代码至关重要。随着Java语言的演进,集合框架也在不断发展,保持学习是成为优秀Java程序员的关键。
Java 集合框架就是代码界的 “收纳大冤种”!List 是排队狂魔,Map 玩记忆游戏,敢用错?程序当场表演 “数据离家出走”!
“本文耗费我3小时+2包薯片,点赞不过分吧?”