一、集合框架概述
1.1 什么是集合框架
Java集合框架(Java Collections Framework, JCF)是Java语言中用于表示和操作集合的一套标准化体系结构。它提供了一组接口、实现类和算法,用于存储和操作对象组,解决了数组在存储对象时的诸多限制。
集合框架的主要优势包括:
减少编程工作量:提供通用的数据结构和算法
提高程序性能:经过优化的实现
促进API互操作性:统一的接口规范
降低学习成本:一致的架构设计
提高软件质量:经过充分测试的代码
1.2 集合框架的体系结构
Java集合框架主要由以下几部分组成:
接口:定义集合的抽象数据类型
实现:接口的具体实现类
算法:对集合进行操作的方法,如排序、搜索等
集合框架的核心接口层次结构如下:
Iterable └── Collection ├── List ├── Set │ └── SortedSet └── Queue └── Deque
此外还有独立的Map
接口及其子接口SortedMap
。
1.3 集合框架的历史演变
Java集合框架经历了几个重要的发展阶段:
JDK 1.0:只有Vector、Hashtable等简单集合类
JDK 1.2:引入完整的集合框架
JDK 1.5:加入泛型支持
JDK 1.6:性能优化和小幅API增强
JDK 1.7:引入钻石操作符等语法糖
JDK 1.8:加入Stream API和Lambda表达式支持
JDK 9:新增不可变集合工厂方法
JDK 10:引入var类型推断
JDK 11+:持续优化和增强
二、核心接口详解
2.1 Collection接口
Collection
是集合框架的根接口,定义了所有集合共有的基本操作:
java
public interface Collection<E> extends Iterable<E> {
// 基本操作
int size();
boolean isEmpty();
boolean contains(Object element);
boolean add(E element);
boolean remove(Object element);
Iterator<E> iterator();
// 批量操作
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear();
// 数组操作
Object[] toArray();
<T> T[] toArray(T[] a);
// JDK 8新增的默认方法
default boolean removeIf(Predicate<? super E> filter) { ... }
default Spliterator<E> spliterator() { ... }
default Stream<E> stream() { ... }
default Stream<E> parallelStream() { ... }
}
2.2 List接口
List
是有序集合(也称为序列),允许重复元素和null值。主要特点包括:
精确控制每个元素的插入位置
可以通过索引访问元素
可以搜索元素
允许对列表进行迭代
java
public interface List<E> extends Collection<E> {
// 位置访问操作
E get(int index);
E set(int index, E element);
void add(int index, E element);
E remove(int index);
int indexOf(Object o);
int lastIndexOf(Object o);
// 列表迭代器
ListIterator<E> listIterator();
ListIterator<E> listIterator(int index);
// 范围视图
List<E> subList(int fromIndex, int toIndex);
// JDK 8新增的默认方法
default void replaceAll(UnaryOperator<E> operator) { ... }
default void sort(Comparator<? super E> c) { ... }
}
2.3 Set接口
Set
是不包含重复元素的集合,最多包含一个null元素。它模拟了数学上的集合概念,不保证元素的顺序(除非使用SortedSet)。
java
public interface Set<E> extends Collection<E> {
// 从Collection继承的方法
// JDK 8新增的默认方法
default Spliterator<E> spliterator() { ... }
}
2.4 Queue接口
Queue
是一种特殊的集合,用于在处理前保存元素。队列通常(但不一定)以FIFO(先进先出)方式排序元素。
java
public interface Queue<E> extends Collection<E> {
// 插入操作
boolean add(E e);
boolean offer(E e);
// 移除操作
E remove();
E poll();
// 检查操作
E element();
E peek();
}
2.5 Map接口
Map
不是真正的集合(不继承Collection接口),但完全集成在集合框架中。它将键映射到值,键不能重复,每个键最多映射到一个值。
java
public interface Map<K,V> {
// 基本操作
int size();
boolean isEmpty();
boolean containsKey(Object key);
boolean containsValue(Object value);
V get(Object key);
V put(K key, V value);
V remove(Object key);
void putAll(Map<? extends K, ? extends V> m);
void clear();
// 集合视图
Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K, V>> entrySet();
// 内部Entry接口
interface Entry<K,V> {
K getKey();
V getValue();
V setValue(V value);
boolean equals(Object o);
int hashCode();
// JDK 8新增的默认方法
public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() { ... }
public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() { ... }
}
// JDK 8新增的默认方法
default V getOrDefault(Object key, V defaultValue) { ... }
default void forEach(BiConsumer<? super K, ? super V> action) { ... }
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { ... }
default V putIfAbsent(K key, V value) { ... }
default boolean remove(Object key, Object value) { ... }
default boolean replace(K key, V oldValue, V newValue) { ... }
default V replace(K key, V value) { ... }
default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { ... }
default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { ... }
default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { ... }
default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { ... }
}
三、主要实现类分析
3.1 List实现类
3.1.1 ArrayList
基于动态数组的实现,非线程安全。特点:
随机访问快(O(1))
尾部插入删除快(O(1))
中间插入删除慢(O(n))
内存占用较少
java
// 典型使用场景
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add(1, "C++"); // 在索引1处插入
// 遍历方式
for (String s : list) {
System.out.println(s);
}
// JDK 8+的forEach方法
list.forEach(System.out::println);
3.1.2 LinkedList
基于双向链表的实现,同时实现了List和Deque接口。特点:
任意位置插入删除快(O(1))
随机访问慢(O(n))
内存占用较多(需要存储前后节点引用)
java
// 典型使用场景
List<Integer> linkedList = new LinkedList<>();
linkedList.add(10);
linkedList.addFirst(5); // 作为Deque使用
linkedList.addLast(15); // 作为Deque使用
// 迭代器遍历
Iterator<Integer> it = linkedList.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
3.1.3 Vector
线程安全的动态数组实现,方法都使用synchronized修饰。已逐渐被ArrayList和Collections.synchronizedList取代。
java
// 使用示例
Vector<String> vector = new Vector<>();
vector.add("Old");
vector.add("Collection");
3.1.4 CopyOnWriteArrayList
线程安全的List实现,适合读多写少的场景。所有修改操作都会创建底层数组的新副本。
java
// 使用示例
CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<>();
cowList.add("Thread-safe");
cowList.add("List");
3.2 Set实现类
3.2.1 HashSet
基于HashMap实现的Set,使用对象的hashCode()方法确定元素位置。特点:
元素无序
添加、删除、包含操作都是O(1)
允许null元素
java
// 使用示例
Set<String> set = new HashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Apple"); // 重复元素不会被添加
System.out.println(set.contains("Apple")); // true
3.2.2 LinkedHashSet
继承自HashSet,但同时维护了一个双向链表来保持插入顺序。特点:
迭代顺序可预测
性能略低于HashSet
适合需要保持插入顺序的场景
java
// 使用示例
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("First");
linkedHashSet.add("Second");
linkedHashSet.add("Third");
// 遍历时会按照插入顺序输出
linkedHashSet.forEach(System.out::println);
3.2.3 TreeSet
基于TreeMap实现的NavigableSet,元素按照自然顺序或Comparator排序。特点:
元素有序
添加、删除、包含操作都是O(log n)
不允许null元素(除非Comparator允许)
java
// 使用示例
Set<Integer> treeSet = new TreeSet<>();
treeSet.add(5);
treeSet.add(2);
treeSet.add(8);
// 遍历时会按照自然顺序输出
treeSet.forEach(System.out::println); // 输出 2,5,8
3.2.4 CopyOnWriteArraySet
基于CopyOnWriteArrayList实现的线程安全Set,适合读多写少的场景。
java
// 使用示例
CopyOnWriteArraySet<String> cowSet = new CopyOnWriteArraySet<>();
cowSet.add("Thread-safe");
cowSet.add("Set");
3.2.5 EnumSet
专为枚举类型设计的高性能Set实现,内部使用位向量表示。
java
// 使用示例
enum Day { MONDAY, TUESDAY, WEDNESDAY }
EnumSet<Day> days = EnumSet.of(Day.MONDAY, Day.WEDNESDAY);
3.3 Queue实现类
3.3.1 LinkedList
如前所述,LinkedList也实现了Deque接口,因此可以作为队列使用。
java
// 作为队列使用
Queue<String> queue = new LinkedList<>();
queue.offer("First");
queue.offer("Second");
String first = queue.poll(); // 移除并返回头部元素
3.3.2 PriorityQueue
基于优先级堆的无界优先级队列,元素按照自然顺序或Comparator排序。
java
// 使用示例
Queue<Integer> priorityQueue = new PriorityQueue<>();
priorityQueue.offer(5);
priorityQueue.offer(1);
priorityQueue.offer(3);
// 出队顺序是1,3,5
while (!priorityQueue.isEmpty()) {
System.out.println(priorityQueue.poll());
}
3.3.3 ArrayDeque
基于循环数组实现的双端队列,比LinkedList更高效。适合用作栈或队列。
java
// 作为栈使用
Deque<String> stack = new ArrayDeque<>();
stack.push("First");
stack.push("Second");
String top = stack.pop(); // "Second"
// 作为队列使用
Deque<String> queue = new ArrayDeque<>();
queue.offer("First");
queue.offer("Second");
String first = queue.poll(); // "First"
3.3.4 BlockingQueue实现
java.util.concurrent包提供了多种阻塞队列实现:
ArrayBlockingQueue:有界阻塞队列
LinkedBlockingQueue:可选有界阻塞队列
PriorityBlockingQueue:无界优先级阻塞队列
SynchronousQueue:不存储元素的阻塞队列
DelayQueue:元素延迟到期的无界阻塞队列
java
// 使用示例
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(10);
// 生产者线程
new Thread(() -> {
try {
blockingQueue.put("Message");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// 消费者线程
new Thread(() -> {
try {
String message = blockingQueue.take();
System.out.println("Received: " + message);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
3.4 Map实现类
3.4.1 HashMap
基于哈希表的Map实现,允许null键和null值。特点:
键无序
基本操作时间复杂度为O(1)
初始容量和负载因子影响性能
java
// 使用示例
Map<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 28);
// 获取值
int age = map.get("Bob"); // 30
// 遍历
map.forEach((name, age) ->
System.out.println(name + " is " + age + " years old"));
3.4.2 LinkedHashMap
继承自HashMap,同时维护了一个双向链表来保持插入顺序或访问顺序。特点:
可以预测的迭代顺序
性能略低于HashMap
适合需要保持插入/访问顺序的场景
java
// 保持插入顺序
Map<String, Integer> linkedMap = new LinkedHashMap<>();
linkedMap.put("First", 1);
linkedMap.put("Second", 2);
linkedMap.put("Third", 3);
// 遍历顺序与插入顺序一致
linkedMap.forEach((k, v) -> System.out.println(k));
// 创建LRU缓存
Map<String, Integer> lruCache = new LinkedHashMap<>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, Integer> eldest) {
return size() > 3;
}
};
3.4.3 TreeMap
基于红黑树实现的NavigableMap,键按照自然顺序或Comparator排序。特点:
键有序
基本操作时间复杂度为O(log n)
不允许null键(除非Comparator允许)
java
// 使用示例
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Orange", 2);
treeMap.put("Apple", 5);
treeMap.put("Banana", 3);
// 遍历顺序是Apple, Banana, Orange
treeMap.forEach((fruit, count) ->
System.out.println(fruit + ": " + count));
3.4.4 Hashtable
线程安全的Map实现,方法都使用synchronized修饰。已逐渐被HashMap和ConcurrentHashMap取代。
java
// 使用示例
Hashtable<String, Integer> table = new Hashtable<>();
table.put("One", 1);
table.put("Two", 2);
3.4.5 ConcurrentHashMap
线程安全的高性能HashMap实现,使用分段锁技术(JDK 8后改为CAS+synchronized)。特点:
高并发性能
不允许null键和null值
操作通常不需要锁定整个表
java
// 使用示例
ConcurrentMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("Key1", 1);
concurrentMap.putIfAbsent("Key1", 2); // 不会替换已有值
// 原子更新
concurrentMap.compute("Key1", (k, v) -> v == null ? 0 : v + 1);
3.4.6 EnumMap
专为枚举键设计的Map实现,内部使用数组存储,非常紧凑和高效。
java
// 使用示例
enum Day { MONDAY, TUESDAY, WEDNESDAY }
Map<Day, String> enumMap = new EnumMap<>(Day.class);
enumMap.put(Day.MONDAY, "First day");
3.4.7 IdentityHashMap
使用==而不是equals比较键的Map实现,适合需要对象标识而非对象值相等的场景。
java
// 使用示例
Map<String, Integer> identityMap = new IdentityHashMap<>();
String key1 = new String("key");
String key2 = new String("key");
identityMap.put(key1, 1);
identityMap.put(key2, 2); // 两个不同的键
System.out.println(identityMap.size()); // 2
3.4.8 WeakHashMap
使用弱引用作为键的Map实现,当键不再被普通引用时,条目会被自动移除。适合实现缓存。
java
// 使用示例
Map<Object, String> weakMap = new WeakHashMap<>();
Object key = new Object();
weakMap.put(key, "value");
key = null; // 使键只被弱引用持有
System.gc(); // 垃圾回收后,条目可能会被移除
四、集合工具类
4.1 Collections工具类
java.util.Collections
提供了大量静态方法,用于操作或返回集合。
4.1.1 排序和搜索
java
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9);
// 排序
Collections.sort(numbers); // [1, 1, 3, 4, 5, 9]
// 自定义排序
Collections.sort(numbers, Comparator.reverseOrder()); // [9, 5, 4, 3, 1, 1]
// 二分查找(列表必须已排序)
int index = Collections.binarySearch(numbers, 4); // 2
// 随机打乱
Collections.shuffle(numbers);
4.1.2 不可变集合
java
// 创建不可变集合
List<String> immutableList = Collections.unmodifiableList(new ArrayList<>());
Set<Integer> immutableSet = Collections.unmodifiableSet(new HashSet<>());
Map<String, Integer> immutableMap = Collections.unmodifiableMap(new HashMap<>());
// JDK 9新增的工厂方法
List<String> list = List.of("a", "b", "c");
Set<Integer> set = Set.of(1, 2, 3);
Map<String, Integer> map = Map.of("a", 1, "b", 2);
4.1.3 同步集合
java
// 创建线程安全集合
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
Set<Integer> syncSet = Collections.synchronizedSet(new HashSet<>());
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
4.1.4 其他实用方法
java
// 填充
List<String> list = new ArrayList<>(Collections.nCopies(3, "default"));
Collections.fill(list, "new"); // ["new", "new", "new"]
// 频率统计
int freq = Collections.frequency(list, "new"); // 3
// 极值
Integer max = Collections.max(numbers);
Integer min = Collections.min(numbers);
// 替换所有
Collections.replaceAll(list, "new", "old");
// 反转
Collections.reverse(list);
// 旋转(将后两个元素移到前面)
Collections.rotate(list, 2);
4.2 Arrays工具类
java.util.Arrays
提供了操作数组的实用方法,许多方法与Collections类似。
java
// 数组转List(返回的List是固定大小的)
List<String> list = Arrays.asList("a", "b", "c");
// 排序
int[] numbers = {3, 1, 4, 1, 5, 9};
Arrays.sort(numbers); // [1, 1, 3, 4, 5, 9]
// 二分查找
int index = Arrays.binarySearch(numbers, 4); // 3
// 填充
Arrays.fill(numbers, 0); // [0, 0, 0, 0, 0, 0]
// 比较
int[] copy = Arrays.copyOf(numbers, numbers.length);
boolean equal = Arrays.equals(numbers, copy); // true
// 字符串表示
String str = Arrays.toString(numbers); // "[0, 0, 0, 0, 0, 0]"
// 并行排序(大数据量时性能更好)
Arrays.parallelSort(new int[] {5, 3, 9, 1});
// JDK 8新增的Stream支持
int sum = Arrays.stream(numbers).sum();
五、集合的迭代与遍历
5.1 迭代器模式
Java集合框架基于迭代器模式,提供了一种统一的方法来遍历各种集合。
5.1.1 Iterator接口
java
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() { throw new UnsupportedOperationException(); }
default void forEachRemaining(Consumer<? super E> action) { ... }
}
基本用法:
java
List<String> list = Arrays.asList("A", "B", "C");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String element = it.next();
System.out.println(element);
if (element.equals("B")) {
it.remove(); // 移除当前元素
}
}
5.1.2 ListIterator接口
ListIterator
扩展了Iterator
,支持双向遍历和修改操作。
java
public interface ListIterator<E> extends Iterator<E> {
boolean hasNext();
E next();
boolean hasPrevious();
E previous();
int nextIndex();
int previousIndex();
void remove();
void set(E e);
void add(E e);
}
使用示例:
java
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
ListIterator<String> lit = list.listIterator();
// 正向遍历
while (lit.hasNext()) {
System.out.println(lit.next());
}
// 反向遍历
while (lit.hasPrevious()) {
String element = lit.previous();
System.out.println(element);
if (element.equals("B")) {
lit.set("B-Updated"); // 修改当前元素
}
}
5.1.3 快速失败(fail-fast)机制
大多数集合的迭代器实现了快速失败机制,当在迭代过程中检测到集合被修改(除了通过迭代器自己的remove方法),会抛出ConcurrentModificationException
。
java
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
// 会抛出ConcurrentModificationException
for (String s : list) {
if (s.equals("B")) {
list.remove(s); // 错误的方式
}
}
// 正确的修改方式
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().equals("B")) {
it.remove(); // 通过迭代器移除
}
}
5.2 遍历集合的各种方式
5.2.1 传统的for循环(适合List)
java
List<String> list = Arrays.asList("A", "B", "C");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
5.2.2 增强的for-each循环
java
for (String s : list) {
System.out.println(s);
}
5.2.3 使用Iterator
java
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
5.2.4 使用ListIterator
java
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
System.out.println(lit.next());
}
5.2.5 使用Java 8的forEach方法
java
list.forEach(System.out::println);
// 或使用Lambda表达式
list.forEach(s -> System.out.println(s));
5.2.6 使用Stream API
java
list.stream().forEach(System.out::println);
// 并行处理
list.parallelStream().forEach(System.out::println);
5.3 Spliterator接口
JDK 8引入的Spliterator
(可分迭代器)用于并行遍历和分割元素序列。
java
public interface Spliterator<T> {
boolean tryAdvance(Consumer<? super T> action);
Spliterator<T> trySplit();
long estimateSize();
int characteristics();
}
使用示例:
java
List<String> list = Arrays.asList("A", "B", "C", "D", "E");
Spliterator<String> spliterator = list.spliterator();
// 尝试分割
Spliterator<String> half = spliterator.trySplit();
// 处理前半部分
half.forEachRemaining(System.out::println); // 输出 A, B
// 处理后半部分
spliterator.forEachRemaining(System.out::println); // 输出 C, D, E
六、集合的性能比较与选择
6.1 主要集合类的性能特征
集合类型 | 实现类 | 获取 | 插入/删除 | 包含 | 有序 | 线程安全 | 允许null |
---|---|---|---|---|---|---|---|
List | ArrayList | O(1) | O(n) | O(n) | 插入顺序 | 否 | 是 |
List | LinkedList | O(n) | O(1) | O(n) | 插入顺序 | 否 | 是 |
List | Vector | O(1) | O(n) | O(n) | 插入顺序 | 是 | 是 |
Set | HashSet | O(1) | O(1) | O(1) | 无 | 否 | 是(1个) |
Set | LinkedHashSet | O(1) | O(1) | O(1) | 插入顺序 | 否 | 是(1个) |
Set | TreeSet | O(log n) | O(log n) | O(log n) | 排序顺序 | 否 | 否 |
Queue | PriorityQueue | O(1) peek | O(log n) offer | - | 排序顺序 | 否 | 否 |
Map | HashMap | O(1) | O(1) | O(1) | 无 | 否 | 是(1个键) |
Map | LinkedHashMap | O(1) | O(1) | O(1) | 插入/访问顺序 | 否 | 是(1个键) |
Map | TreeMap | O(log n) | O(log n) | O(log n) | 排序顺序 | 否 | 否(键) |
Map | Hashtable | O(1) | O(1) | O(1) | 无 | 是 | 否 |
Map | ConcurrentHashMap | O(1) | O(1) | O(1) | 无 | 是 | 否 |
6.2 如何选择合适的集合
选择集合时应考虑以下因素:
元素是否需要有序:
需要保持插入顺序:LinkedHashSet、LinkedHashMap、ArrayList、LinkedList
需要排序:TreeSet、TreeMap、PriorityQueue
不需要顺序:HashSet、HashMap
是否需要允许重复元素:
允许重复:List及其实现类
不允许重复:Set及其实现类
性能需求:
随机访问多:ArrayList
插入删除多:LinkedList
快速查找:HashSet/HashMap
线程安全需求:
需要线程安全:ConcurrentHashMap、CopyOnWriteArrayList、Collections.synchronizedXxx
不需要线程安全:标准实现类
null值处理:
需要存储null:ArrayList、HashSet、HashMap等
不能存储null:Hashtable、ConcurrentHashMap、EnumSet/Map等
内存占用:
内存敏感:ArrayList比LinkedList更节省空间
EnumSet/Map非常紧凑高效
6.3 集合初始容量与负载因子
对于基于哈希表的集合(HashMap、HashSet等),初始容量和负载因子影响性能:
初始容量:哈希表创建时的桶数量。默认16。
负载因子:哈希表自动扩容前的填充比例。默认0.75。
java
// 自定义初始容量和负载因子
Map<String, Integer> map = new HashMap<>(32, 0.5f);
合理设置这些参数可以减少rehash操作,提高性能:
如果知道元素数量,设置初始容量为预期数量的1.33倍(1/0.75)
高负载因子减少内存使用但增加查找成本
低负载因子提高查找性能但增加内存使用
七、Java 8/9/10对集合的增强
7.1 Java 8的增强
7.1.1 Stream API
Stream API为集合提供了强大的数据操作能力:
java
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// 过滤和映射
List<String> result = names.stream()
.filter(name -> name.length() > 4)
.map(String::toUpperCase)
.collect(Collectors.toList());
// 统计
long count = names.stream().filter(name -> name.startsWith("A")).count();
// 并行处理
int totalLength = names.parallelStream()
.mapToInt(String::length)
.sum();
7.1.2 forEach方法
所有集合都新增了forEach
方法:
java
Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
// 遍历Map
map.forEach((k, v) -> System.out.println(k + "=" + v));
7.1.3 Map的增强方法
Map接口新增了许多实用方法:
java
Map<String, Integer> map = new HashMap<>();
// 键不存在时才放入
map.putIfAbsent("A", 1);
// 根据键计算新值
map.compute("A", (k, v) -> v == null ? 0 : v + 1);
// 合并值
map.merge("A", 1, (oldVal, newVal) -> oldVal + newVal);
// 获取或默认值
int val = map.getOrDefault("B", 0);
7.1.4 Collection的removeIf方法
java
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
// 移除所有偶数
numbers.removeIf(n -> n % 2 == 0); // [1, 3, 5]
7.2 Java 9的增强
7.2.1 不可变集合的工厂方法
Java 9引入了简洁的创建不可变集合的方法:
java
// 不可变List
List<String> immutableList = List.of("a", "b", "c");
// 不可变Set
Set<Integer> immutableSet = Set.of(1, 2, 3);
// 不可变Map
Map<String, Integer> immutableMap = Map.of("a", 1, "b", 2);
// 更多元素的Map
Map<String, Integer> map = Map.ofEntries(
Map.entry("a", 1),
Map.entry("b", 2),
Map.entry("c", 3)
);
这些集合:
不允许null元素
创建后不可修改
空间优化(可能使用专用内部类)
7.2.2 其他增强
Optional
增加了stream()
方法新增
Collectors.filtering
和Collectors.flatMapping
7.3 Java 10的增强
7.3.1 不可变集合的copyOf方法
java
List<String> original = new ArrayList<>();
List<String> copy = List.copyOf(original); // 不可变副本
7.3.2 var关键字与集合
虽然var不是集合特有的,但它简化了集合声明:
java
var list = new ArrayList<String>(); // 推断为ArrayList<String>
var map = new HashMap<Integer, String>(); // 推断为HashMap<Integer, String>
八、集合的最佳实践
8.1 通用最佳实践
使用接口类型声明集合:
java
// 好 List<String> list = new ArrayList<>(); // 不好 ArrayList<String> list = new ArrayList<>();
预估集合大小:
java
// 如果知道大约需要存储1000个元素 List<String> list = new ArrayList<>(1000);
使用isEmpty()而不是size()==0:
java
// 好 if (list.isEmpty()) { ... } // 不好 if (list.size() == 0) { ... }
优先使用for-each循环:
java
for (String s : list) { ... }
注意集合的equals和hashCode:
不同集合类的equals行为不同
修改作为Map键的对象时要小心
8.2 并发场景下的最佳实践
选择合适的并发集合:
高并发读取:
ConcurrentHashMap
写少读多:
CopyOnWriteArrayList
阻塞队列:
ArrayBlockingQueue
等
避免在迭代时修改集合:
使用并发集合或同步块
或者先创建副本再迭代
注意原子复合操作:
java
// 非原子操作 if (!map.containsKey(key)) { map.put(key, value); } // 使用原子方法 map.putIfAbsent(key, value);
8.3 性能优化建议
选择合适的集合类型:
随机访问多:ArrayList
插入删除多:LinkedList
合理设置HashMap/HashSet初始容量:
java
// 预期存储1000个元素,负载因子0.75 Map<String, Integer> map = new HashMap<>(1333); // 1000 / 0.75
考虑使用EnumSet/EnumMap:
枚举集合非常高效
避免频繁的装箱拆箱:
考虑使用Trove、FastUtil等第三方库
批量操作优于单元素操作:
java
// 好 list.addAll(otherList); // 不好 for (String s : otherList) { list.add(s); }
8.4 常见陷阱与避免方法
ConcurrentModificationException:
避免在迭代时直接修改集合
使用迭代器的remove方法或并发集合
可变对象作为Map键:
如果键对象可变,修改后可能导致找不到值
建议使用不可变对象作为键
equals和hashCode不一致:
确保相等的对象有相同的hashCode
重写equals时必须重写hashCode
原始集合的泛型使用:
避免使用原始类型集合
总是指定泛型参数
内存泄漏:
长期存活的集合可能导致内存泄漏
考虑使用WeakHashMap或定期清理
九、集合框架的扩展与替代方案
9.1 第三方集合库
9.1.1 Google Guava
Google的Guava库提供了丰富的集合工具:
java
// 不可变集合
ImmutableList<String> immutableList = ImmutableList.of("a", "b", "c");
// 多值Map
Multimap<String, Integer> multimap = ArrayListMultimap.create();
multimap.put