Collection 与 Map 两大体系的接口层级与职责划分List / Set / Queue 的行为差异与典型实现类(ArrayList / LinkedList / HashSet / TreeSet / PriorityQueue)Iterator 迭代器的 fail-fast(快速失败)机制与 ConcurrentModificationExceptionjava.util 包中提供一些集合类,这些集合类又被称为容器。提到容器不难想到数组。集合类与数组的不同之处是:数组的长度是固定的,集合的长度是可变的;数组用来存放基本类型的数据,集合用来存放对象的引用。
常用的集合有 List 集合、Set 集合、Queue 队列和 Map 集合,其中 List 集合、Set 集合与 Queue 队列继承 Collection 接口,各接口还提供了不同的实现类:
List:一种有序列表的集合,例如,按索引排列的 Student 的 ListSet:一种保证没有重复元素的集合,例如,所有无重复名称的 Student 的 SetQueue:一种先进先出的队列集合,例如,任务队列、消息队列Map:一种通过键值 (key-value) 查找的映射表集合,例如,根据 Student 的 name 查找对应 Student 的 MapJava 集合框架主要由两个根接口派生而来:
Collection 接口:存储单个元素
Map 接口:存储键值对
Iterable (接口)
└── Collection (接口)
├── List (接口)
│ ├── ArrayList
│ ├── LinkedList
│ └── Vector
│ └── Stack
├── Set (接口)
│ ├── HashSet
│ │ └── LinkedHashSet
│ └── TreeSet
└── Queue (接口)
├── LinkedList
├── PriorityQueue
└── ArrayDeque
Map (接口)
├── HashMap
│ └── LinkedHashMap
├── TreeMap
├── Hashtable
│ └── Properties
└── ConcurrentHashMapList 是一个有序集合,允许存储重复的元素,并且可以通过索引访问元素。它是 Collection 接口的子接口,提供了比 Collection 更丰富的操作方法。
List 接口继承自 Collection 接口,定义了一个有序的集合,支持以下特性:
List 接口扩展了 Collection 接口的方法,并添加了一些与索引相关的操作。
| 方法名 | 功能描述 |
|---|---|
boolean add(E e) | 在列表末尾添加一个元素 |
void add(int index, E element) | 在指定索引位置插入一个元素 |
boolean addAll(Collection<? extends E> c) | 将指定集合中的所有元素添加到列表末尾 |
boolean addAll(int index, Collection<? extends E> c) | 将指定集合中的所有元素插入到指定索引位置 |
| 方法名 | 功能描述 |
|---|---|
E remove(int index) | 移除并返回指定索引位置的元素 |
boolean remove(Object o) | 移除列表中第一个匹配的元素(如果存在) |
boolean removeAll(Collection<?> c) | 移除列表中包含在指定集合中的所有元素 |
void clear() | 清空列表中的所有元素 |
| 方法名 | 功能描述 |
|---|---|
E set(int index, E element) | 替换指定索引位置的元素,并返回被替换的元素 |
| 方法名 | 功能描述 |
|---|---|
E get(int index) | 返回指定索引位置的元素 |
int indexOf(Object o) | 返回指定元素的第一次出现的索引(如果不存在,返回 -1) |
int lastIndexOf(Object o) | 返回指定元素的最后一次出现的索引(如果不存在,返回 -1) |
List<E> subList(int fromIndex, int toIndex) | 返回从 fromIndex 到 toIndex 的子列表(左闭右开区间) |
| 方法名 | 功能描述 |
|---|---|
Iterator<E> iterator() | 返回一个迭代器,用于遍历列表中的元素 |
ListIterator<E> listIterator() | 返回一个双向迭代器,支持从前往后和从后往前遍历列表 |
ListIterator<E> listIterator(int index) | 返回一个从指定索引开始的双向迭代器 |
| 方法名 | 功能描述 |
|---|---|
default void sort(Comparator<? super E> c) | 对列表中的元素进行排序(需要 Java 8+)。 |
boolean containsAll(Collection<?> c) | 判断列表是否包含指定集合中的所有元素。 |
boolean equals(Object o) | 比较两个列表是否相等(元素顺序和内容相同)。 |
int hashCode() | 返回列表的哈希码值。 |
| 方法名 | 功能描述 |
|---|---|
Spliterator<E> spliterator() | 返回一个 Spliterator,用于并行遍历列表中的元素。 |
Stream<E> stream() | 返回一个顺序流(Stream),用于对列表进行流式操作。 |
Stream<E> parallelStream() | 返回一个并行流(Parallel Stream),用于并行处理列表中的元素。 |
List 接口有多个实现类,每个实现类都有不同的特性和适用场景。
import java.util.ArrayList;
import java.util.List;
public class ArrayListExample {
public static void main(String[] args) {
List<String> arrayList = new ArrayList<>();
arrayList.add("Apple");
arrayList.add("Banana");
arrayList.add("Cherry");
System.out.println("ArrayList: " + arrayList); // 输出: [Apple, Banana, Cherry]
System.out.println("Element at index 1: " + arrayList.get(1)); // 输出: Banana
}
}ArrayList 扩容机制:
int newCapacity = oldCapacity + (oldCapacity >> 1))List 和 Deque 接口,可以作为列表、队列、栈使用import java.util.LinkedList;
import java.util.List;
public class LinkedListExample {
public static void main(String[] args) {
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("Apple");
linkedList.add("Banana");
linkedList.add("Cherry");
System.out.println("LinkedList: " + linkedList); // 输出: [Apple, Banana, Cherry]
System.out.println("First Element: " + linkedList.getFirst()); // 输出: Apple
System.out.println("Last Element: " + linkedList.getLast()); // 输出: Cherry
// 作为栈使用
linkedList.push("Durian"); // 等同于 addFirst
System.out.println("Stack pop: " + linkedList.pop()); // 等同于 removeFirst
// 作为队列使用
linkedList.offer("Elderberry"); // 等同于 add/addLast
System.out.println("Queue poll: " + linkedList.poll()); // 等同于 removeFirst
}
}synchronized 修饰)ArrayList 取代CopyOnWriteArrayList)import java.util.Vector;
public class VectorExample {
public static void main(String[] args) {
Vector<String> vector = new Vector<>();
vector.add("Apple");
vector.add("Banana");
System.out.println("Vector: " + vector);
// Stack 是 Vector 的子类
java.util.Stack<String> stack = new java.util.Stack<>();
stack.push("A");
stack.push("B");
System.out.println("Stack pop: " + stack.pop()); // 输出: B
}
}import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListExample {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("Apple");
list.add("Banana");
System.out.println("List: " + list); // 输出: [Apple, Banana]
// 适合并发遍历
list.forEach(System.out::println);
}
}for 循环import java.util.ArrayList;
import java.util.List;
public class ForLoopExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}import java.util.ArrayList;
import java.util.List;
public class ForEachLoopExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
for (String item : list) {
System.out.println(item);
}
}
}forEach 方法(Java 8+)import java.util.ArrayList;
import java.util.List;
public class ForEachExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.forEach(System.out::println);
}
}import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class ListIteratorExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
// 正向遍历
ListIterator<String> iterator = list.listIterator();
while (iterator.hasNext()) {
System.out.println("Index " + iterator.nextIndex() + ": " + iterator.next());
}
// 反向遍历
System.out.println("\nReverse traversal:");
while (iterator.hasPrevious()) {
System.out.println("Index " + iterator.previousIndex() + ": " + iterator.previous());
}
}
}import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SortExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Banana");
list.add("Apple");
list.add("Cherry");
Collections.sort(list); // 默认按自然顺序排序
System.out.println("Sorted List: " + list); // 输出: [Apple, Banana, Cherry]
}
}import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class CustomSortExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Banana");
list.add("Apple");
list.add("Cherry");
list.sort(Comparator.reverseOrder()); // 按降序排序
System.out.println("Reverse Sorted List: " + list); // 输出: [Cherry, Banana, Apple]
}
}示例:过滤和映射
import java.util.ArrayList;
import java.util.List;
public class StreamExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
// 过滤以 'A' 开头的元素并转换为大写
// (Stream.toList() 为 Java 16+;Java 8/11 用 collect(Collectors.toList()))
List<String> result = list.stream()
.filter(s -> s.startsWith("A"))
.map(String::toUpperCase)
.toList();
System.out.println(result); // 输出: [APPLE]
}
}Queue 是一个**先进先出(FIFO)**的队列集合,继承自 Collection 接口。队列通常用于存储待处理的任务或消息。
| 方法名 | 功能描述 | 失败时行为 |
|---|---|---|
boolean add(E e) | 添加元素到队列尾部 | 抛出异常 |
boolean offer(E e) | 添加元素到队列尾部 | 返回 false |
E remove() | 移除并返回队列头部元素 | 抛出异常 |
E poll() | 移除并返回队列头部元素 | 返回 null |
E element() | 返回队列头部元素但不移除 | 抛出异常 |
E peek() | 返回队列头部元素但不移除 | 返回 null |
List 和 Deque 接口import java.util.LinkedList;
import java.util.Queue;
public class LinkedListQueueExample {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
queue.offer("A");
queue.offer("B");
queue.offer("C");
System.out.println("Queue: " + queue); // 输出: [A, B, C]
System.out.println("Poll: " + queue.poll()); // 输出: A
System.out.println("Peek: " + queue.peek()); // 输出: B
}
}import java.util.PriorityQueue;
import java.util.Queue;
public class PriorityQueueExample {
public static void main(String[] args) {
// 默认最小堆(元素按从小到大排序)
Queue<Integer> queue = new PriorityQueue<>();
queue.offer(5);
queue.offer(1);
queue.offer(3);
System.out.println("PriorityQueue: " + queue);
// 按优先级取出元素
while (!queue.isEmpty()) {
System.out.println(queue.poll());
}
// 输出顺序: 1, 3, 5
// 自定义比较器:最大堆
Queue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
maxHeap.offer(5);
maxHeap.offer(1);
maxHeap.offer(3);
System.out.println("\nMax Heap:");
while (!maxHeap.isEmpty()) {
System.out.println(maxHeap.poll());
}
// 输出顺序: 5, 3, 1
}
}Stack 和 LinkedListimport java.util.ArrayDeque;
import java.util.Deque;
public class ArrayDequeExample {
public static void main(String[] args) {
// 作为栈使用
Deque<String> stack = new ArrayDeque<>();
stack.push("A");
stack.push("B");
stack.push("C");
System.out.println("Stack pop: " + stack.pop()); // 输出: C
System.out.println("Stack peek: " + stack.peek()); // 输出: B
// 作为队列使用
Deque<String> queue = new ArrayDeque<>();
queue.offer("A");
queue.offer("B");
queue.offer("C");
System.out.println("\nQueue poll: " + queue.poll()); // 输出: A
System.out.println("Queue peek: " + queue.peek()); // 输出: B
}
}Set 是一个不包含重复元素的集合,它继承自 Collection 接口。Set 接口不保证元素的顺序,但某些实现类(如 LinkedHashSet)可以维护插入顺序。
Set 接口的主要特性:
Set 接口继承自 Collection 接口,没有添加新的方法,但重写了一些方法以确保不重复的特性。
| 方法名 | 功能描述 |
|---|---|
boolean add(E e) | 添加元素,如果元素已存在则返回 false |
boolean remove(Object o) | 移除指定元素,如果元素存在则返回 true |
boolean contains(Object o) | 判断集合是否包含指定元素 |
int size() | 返回集合中的元素数量 |
boolean isEmpty() | 判断集合是否为空 |
Iterator<E> iterator() | 返回迭代器,用于遍历集合中的元素 |
Object[] toArray() | 将集合转换为数组 |
boolean containsAll(Collection<?> c) | 判断集合是否包含指定集合中的所有元素 |
boolean addAll(Collection<? extends E> c) | 将指定集合中的所有元素添加到集合中 |
boolean retainAll(Collection<?> c) | 仅保留集合中包含在指定集合中的元素 |
boolean removeAll(Collection<?> c) | 移除集合中包含在指定集合中的所有元素 |
void clear() | 移除集合中的所有元素 |
HashMap 实例)import java.util.HashSet;
import java.util.Set;
public class HashSetExample {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Cherry");
set.add("Apple"); // 重复元素,不会被添加
System.out.println("HashSet: " + set); // 输出顺序可能不同
System.out.println("Contains 'Apple': " + set.contains("Apple")); // 输出: true
System.out.println("Size: " + set.size()); // 输出: 3
}
}HashSet 判断元素重复的原理:
hashCode() 方法计算哈希值equals() 方法比较HashSetHashSet,但仍然很好import java.util.LinkedHashSet;
import java.util.Set;
public class LinkedHashSetExample {
public static void main(String[] args) {
Set<String> set = new LinkedHashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Cherry");
System.out.println("LinkedHashSet: " + set); // 输出: [Apple, Banana, Cherry],保持插入顺序
}
}import java.util.TreeSet;
import java.util.Set;
public class TreeSetExample {
public static void main(String[] args) {
Set<String> set = new TreeSet<>();
set.add("Banana");
set.add("Apple");
set.add("Cherry");
System.out.println("TreeSet: " + set); // 输出: [Apple, Banana, Cherry],按字母顺序排序
// 自定义比较器
Set<String> reverseSet = new TreeSet<>((a, b) -> b.compareTo(a));
reverseSet.add("Banana");
reverseSet.add("Apple");
reverseSet.add("Cherry");
System.out.println("Reverse TreeSet: " + reverseSet); // 输出: [Cherry, Banana, Apple]
}
}import java.util.EnumSet;
import java.util.Set;
public class EnumSetExample {
enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }
public static void main(String[] args) {
Set<Day> weekend = EnumSet.of(Day.SATURDAY, Day.SUNDAY);
Set<Day> weekdays = EnumSet.range(Day.MONDAY, Day.FRIDAY);
System.out.println("Weekend: " + weekend); // 输出: [SATURDAY, SUNDAY]
System.out.println("Weekdays: " + weekdays); // 输出: [MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY]
}
}import java.util.HashSet;
import java.util.Set;
public class SetOperationsExample {
public static void main(String[] args) {
Set<Integer> set1 = new HashSet<>();
Set<Integer> set2 = new HashSet<>();
// 添加元素
set1.add(1);
set1.add(2);
set1.add(3);
set2.add(2);
set2.add(3);
set2.add(4);
// 并集
Set<Integer> union = new HashSet<>(set1);
union.addAll(set2);
System.out.println("Union: " + union); // 输出: [1, 2, 3, 4]
// 交集
Set<Integer> intersection = new HashSet<>(set1);
intersection.retainAll(set2);
System.out.println("Intersection: " + intersection); // 输出: [2, 3]
// 差集
Set<Integer> difference = new HashSet<>(set1);
difference.removeAll(set2);
System.out.println("Difference: " + difference); // 输出: [1]
}
}import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
class Student {
private String id;
private String name;
public Student(String id, String name) {
this.id = id;
this.name = name;
}
// 必须重写 equals() 和 hashCode() 方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(id, student.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
return "Student{id='" + id + "', name='" + name + "'}";
}
}
public class CustomObjectSetExample {
public static void main(String[] args) {
Set<Student> students = new HashSet<>();
students.add(new Student("001", "张三"));
students.add(new Student("002", "李四"));
students.add(new Student("001", "王五")); // 不会添加,因为 id 相同
System.out.println("Students: " + students);
}
}Map 是一种存储键值对(key-value)的集合,它不继承自 Collection 接口。每个键最多只能映射到一个值,键不能重复,但值可以重复。
Map 接口的主要特性:
| 方法名 | 功能描述 |
|---|---|
V put(K key, V value) | 添加键值对,如果键已存在则替换旧值 |
V get(Object key) | 返回指定键映射的值,如果不存在则返回 null |
V remove(Object key) | 移除指定键的映射关系,并返回对应的值 |
boolean containsKey(Object key) | 判断是否包含指定键 |
boolean containsValue(Object value) | 判断是否包含指定值 |
int size() | 返回键值对的数量 |
boolean isEmpty() | 判断是否为空 |
void clear() | 清空所有键值对 |
Set<K> keySet() | 返回所有键的集合 |
Collection<V> values() | 返回所有值的集合 |
Set<Map.Entry<K, V>> entrySet() | 返回所有键值对的集合 |
void putAll(Map<? extends K, ? extends V> m) | 将指定映射中的所有映射关系添加到此映射中 |
| 方法名 | 功能描述 |
|---|---|
V getOrDefault(Object key, V defaultValue) | 获取指定键的值,如果不存在则返回默认值 |
V putIfAbsent(K key, V value) | 如果键不存在才添加 |
boolean remove(Object key, Object value) | 仅当键和值都匹配时才移除 |
boolean replace(K key, V oldValue, V newValue) | 仅当键和旧值都匹配时才替换 |
V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) | 如果键不存在,计算并添加值 |
V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) | 如果键存在,重新计算值 |
V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) | 合并键值对 |
import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 10);
map.put("Banana", 20);
map.put("Cherry", 30);
map.put("Apple", 15); // 替换旧值
System.out.println("Map: " + map);
System.out.println("Value of 'Apple': " + map.get("Apple")); // 输出: 15
System.out.println("Contains key 'Banana': " + map.containsKey("Banana")); // 输出: true
System.out.println("Size: " + map.size()); // 输出: 3
// Java 8+ 新方法
System.out.println("Value of 'Date': " + map.getOrDefault("Date", 0)); // 输出: 0
map.putIfAbsent("Banana", 25); // 不会替换,因为键已存在
map.putIfAbsent("Date", 40); // 会添加
System.out.println("After putIfAbsent: " + map);
}
}HashMap 扩容机制:
HashMapHashMap,但仍然很好import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapExample {
public static void main(String[] args) {
// 按插入顺序
Map<String, Integer> map = new LinkedHashMap<>();
map.put("Apple", 10);
map.put("Banana", 20);
map.put("Cherry", 30);
System.out.println("LinkedHashMap: " + map); // 输出: {Apple=10, Banana=20, Cherry=30},保持插入顺序
// 按访问顺序(可用于实现 LRU 缓存)
Map<String, Integer> lruMap = new LinkedHashMap<>(16, 0.75f, true);
lruMap.put("A", 1);
lruMap.put("B", 2);
lruMap.put("C", 3);
lruMap.get("A"); // 访问 A
System.out.println("LRU Map: " + lruMap); // 输出: {B=2, C=3, A=1},A 移到最后
}
}import java.util.Map;
import java.util.TreeMap;
public class TreeMapExample {
public static void main(String[] args) {
Map<String, Integer> map = new TreeMap<>();
map.put("Banana", 20);
map.put("Apple", 10);
map.put("Cherry", 30);
System.out.println("TreeMap: " + map); // 输出: {Apple=10, Banana=20, Cherry=30},按键排序
// 导航方法
TreeMap<String, Integer> treeMap = new TreeMap<>(map);
System.out.println("First key: " + treeMap.firstKey()); // 输出: Apple
System.out.println("Last key: " + treeMap.lastKey()); // 输出: Cherry
System.out.println("Lower key of 'Banana': " + treeMap.lowerKey("Banana")); // 输出: Apple
System.out.println("Higher key of 'Banana': " + treeMap.higherKey("Banana")); // 输出: Cherry
}
}HashMapConcurrentHashMap)import java.util.Hashtable;
import java.util.Map;
public class HashtableExample {
public static void main(String[] args) {
Map<String, Integer> map = new Hashtable<>();
map.put("Apple", 10);
map.put("Banana", 20);
map.put("Cherry", 30);
System.out.println("Hashtable: " + map);
}
}synchronized(锁单个桶头节点)Hashtableimport java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapExample {
public static void main(String[] args) {
Map<String, Integer> map = new ConcurrentHashMap<>();
map.put("Apple", 10);
map.put("Banana", 20);
map.put("Cherry", 30);
System.out.println("ConcurrentHashMap: " + map);
// 线程安全的操作
map.computeIfAbsent("Date", k -> 40);
System.out.println("After computeIfAbsent: " + map);
}
}import java.util.HashMap;
import java.util.Map;
public class MapTraversalExample {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 10);
map.put("Banana", 20);
map.put("Cherry", 30);
// 遍历键
System.out.println("Keys:");
for (String key : map.keySet()) {
System.out.println(key);
}
// 遍历值
System.out.println("\nValues:");
for (Integer value : map.values()) {
System.out.println(value);
}
// 遍历键值对(推荐)
System.out.println("\nEntries:");
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
// Java 8+ forEach 方法
System.out.println("\nUsing forEach:");
map.forEach((key, value) -> System.out.println(key + " = " + value));
}
}import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
class Person {
private String id;
private String name;
public Person(String id, String name) {
this.id = id;
this.name = name;
}
// 必须重写 equals() 和 hashCode() 方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(id, person.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
return "Person{id='" + id + "', name='" + name + "'}";
}
}
public class CustomKeyMapExample {
public static void main(String[] args) {
Map<Person, String> map = new HashMap<>();
map.put(new Person("001", "张三"), "工程师");
map.put(new Person("002", "李四"), "设计师");
map.put(new Person("001", "王五"), "教师"); // 替换旧值,因为 id 相同
System.out.println("Map:");
map.forEach((person, job) -> System.out.println(person + " -> " + job));
}
}迭代器(Iterator)是一种设计模式,用于遍历集合中的元素。Java 集合框架中的 Collection 接口继承了 Iterable 接口,因此所有集合都可以使用迭代器遍历。
Iterator 接口提供了基本的遍历功能:
| 方法名 | 功能描述 |
|---|---|
boolean hasNext() | 判断是否还有下一个元素 |
E next() | 返回下一个元素 |
default void remove() | 移除当前元素(可选操作) |
default void forEachRemaining(Consumer<? super E> action) | 对剩余元素执行操作(Java 8+) |
ListIterator 继承自 Iterator,提供了双向遍历和修改功能:
| 方法名 | 功能描述 |
|---|---|
boolean hasPrevious() | 判断是否还有前一个元素 |
E previous() | 返回前一个元素 |
int nextIndex() | 返回下一个元素的索引 |
int previousIndex() | 返回前一个元素的索引 |
void set(E e) | 替换当前元素 |
void add(E e) | 在当前位置插入元素 |
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class IteratorExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
// 使用 Iterator 遍历并删除元素
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
if ("B".equals(element)) {
iterator.remove(); // 安全删除
}
}
System.out.println("After remove: " + list); // 输出: [A, C]
// 使用 ListIterator 遍历并修改元素
ListIterator<String> listIterator = list.listIterator();
while (listIterator.hasNext()) {
String element = listIterator.next();
listIterator.set(element.toLowerCase());
}
System.out.println("After set: " + list); // 输出: [a, c]
// 反向遍历
System.out.println("\nReverse:");
while (listIterator.hasPrevious()) {
System.out.println(listIterator.previous());
}
}
}remove/add/set)修改集合,会抛出 ConcurrentModificationExceptionimport java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ConcurrentModificationExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
// 错误:在迭代过程中通过集合方法修改
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
if ("A".equals(element)) {
// list.remove(element); // 抛出 ConcurrentModificationException
}
}
// 正确:使用迭代器的 remove 方法
iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
if ("A".equals(element)) {
iterator.remove(); // 安全
}
}
}
}import java.util.ArrayList;
import java.util.List;
public class ForEachLimitationExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
// 错误:会抛出 ConcurrentModificationException
// (注:若恰好删除的是倒数第二个元素,迭代器不再调用 next(),可能不抛异常而静默漏删)
for (String element : list) {
if ("A".equals(element)) {
// list.remove(element); // 错误
}
}
// 正确:使用迭代器或 removeIf(Java 8+)
list.removeIf(element -> "A".equals(element));
System.out.println("After removeIf: " + list); // 输出: [B, C]
}
}大多数集合类(如 ArrayList、HashMap、HashSet)都是非线程安全的。在多线程环境下使用这些集合可能导致:
HashMap 在并发扩容时)ConcurrentModificationExceptionCollections 工具类提供了同步包装器方法:
import java.util.*;
public class SynchronizedCollectionExample {
public static void main(String[] args) {
// 同步的 List
List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());
// 同步的 Set
Set<String> synchronizedSet = Collections.synchronizedSet(new HashSet<>());
// 同步的 Map
Map<String, Integer> synchronizedMap = Collections.synchronizedMap(new HashMap<>());
// 注意:迭代时需要手动同步
synchronized (synchronizedList) {
for (String element : synchronizedList) {
System.out.println(element);
}
}
}
}java.util.concurrent 包提供了高性能的并发集合:
import java.util.*;
import java.util.concurrent.*;
public class ConcurrentCollectionExample {
public static void main(String[] args) {
// 并发的 List
List<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
// 并发的 Set
Set<String> copyOnWriteArraySet = new CopyOnWriteArraySet<>();
// 并发的 Map
Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();
// 并发的 Queue
Queue<String> concurrentLinkedQueue = new ConcurrentLinkedQueue<>();
// 阻塞队列
BlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(10);
BlockingQueue<String> linkedBlockingQueue = new LinkedBlockingQueue<>();
}
}synchronized 关键字import java.util.*;
public class SynchronizedBlockExample {
private List<String> list = new ArrayList<>();
public void add(String element) {
synchronized (list) {
list.add(element);
}
}
public String get(int index) {
synchronized (list) {
return list.get(index);
}
}
}| 集合类型 | 读性能 | 写性能 | 适用场景 |
|---|---|---|---|
Hashtable | 低 | 低 | 不推荐使用 |
Collections.synchronizedXxx | 中 | 中 | 需要完全同步的场景 |
ConcurrentHashMap | 高 | 高 | 高并发读写场景(推荐) |
CopyOnWriteArrayList | 高 | 低 | 读多写少的场景 |
Arrays 类提供了操作数组的静态方法。
| 方法名 | 功能描述 |
|---|---|
static <T> List<T> asList(T... a) | 将数组转为 List(固定大小) |
static void sort(int[] a) | 对数组排序 |
static int binarySearch(int[] a, int key) | 二分查找(数组必须有序) |
static void fill(int[] a, int val) | 填充数组 |
static int[] copyOf(int[] original, int newLength) | 复制数组 |
static String toString(int[] a) | 数组转字符串 |
static boolean equals(int[] a, int[] a2) | 比较数组是否相等 |
import java.util.Arrays;
import java.util.List;
public class ArraysExample {
public static void main(String[] args) {
// asList:数组转 List
String[] array = {"A", "B", "C"};
List<String> list = Arrays.asList(array);
System.out.println("List: " + list);
// 注意:asList 返回的 List 大小固定,不能添加或删除元素
// list.add("D"); // 抛出 UnsupportedOperationException
// 如果需要可变 List,需要用 new ArrayList<>(Arrays.asList(array))
// sort:排序
int[] numbers = {3, 1, 4, 1, 5, 9};
Arrays.sort(numbers);
System.out.println("Sorted: " + Arrays.toString(numbers));
// binarySearch:二分查找
int index = Arrays.binarySearch(numbers, 4);
System.out.println("Index of 4: " + index);
// fill:填充
int[] filled = new int[5];
Arrays.fill(filled, 10);
System.out.println("Filled: " + Arrays.toString(filled));
// copyOf:复制
int[] copied = Arrays.copyOf(numbers, 3);
System.out.println("Copied: " + Arrays.toString(copied));
// equals:比较
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println("Equals: " + Arrays.equals(a, b)); // 输出: true
}
}Collections 类提供了操作集合的静态方法,包括排序、搜索、同步等。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsExample {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(3);
list.add(1);
list.add(4);
list.add(2);
System.out.println("Original list: " + list);
// 排序
Collections.sort(list);
System.out.println("Sorted list: " + list);
// 反转
Collections.reverse(list);
System.out.println("Reversed list: " + list);
// 洗牌
Collections.shuffle(list);
System.out.println("Shuffled list: " + list);
// 查找最大值和最小值
System.out.println("Max: " + Collections.max(list));
System.out.println("Min: " + Collections.min(list));
// 查找元素索引(列表必须有序)
Collections.sort(list);
System.out.println("Binary search: " + Collections.binarySearch(list, 3));
// 替换所有元素
Collections.replaceAll(list, 3, 10);
System.out.println("After replaceAll: " + list);
// 填充
Collections.fill(list, 0);
System.out.println("After fill: " + list);
// 创建不可修改的集合
List<Integer> unmodifiableList = Collections.unmodifiableList(new ArrayList<>(list));
// unmodifiableList.add(5); // 抛出 UnsupportedOperationException
// 创建同步的集合
List<Integer> synchronizedList = Collections.synchronizedList(list);
// 创建单元素集合
List<Integer> singletonList = Collections.singletonList(1);
System.out.println("Singleton list: " + singletonList);
// 创建空集合
List<Integer> emptyList = Collections.emptyList();
System.out.println("Empty list: " + emptyList);
}
}泛型提供了编译时类型安全,允许在集合中存储特定类型的对象。
import java.util.ArrayList;
import java.util.List;
public class GenericCollectionExample {
public static void main(String[] args) {
// 使用泛型指定集合中存储的元素类型
List<String> stringList = new ArrayList<>();
stringList.add("Hello");
stringList.add("World");
// stringList.add(123); // 编译错误,不能添加非 String 类型的元素
// 泛型方法
List<Integer> intList = createList(1, 2, 3);
System.out.println("Integer list: " + intList);
}
// 泛型方法
@SafeVarargs
public static <T> List<T> createList(T... items) {
List<T> list = new ArrayList<>();
for (T item : items) {
list.add(item);
}
return list;
}
}Java 8 引入了许多集合操作的新特性,特别是 Stream API。
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Java8CollectionFeatures {
public static void main(String[] args) {
List<String> list = Arrays.asList("Apple", "Banana", "Cherry", "Date");
// 过滤和转换
List<String> filtered = list.stream()
.filter(s -> s.length() > 5)
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println("Filtered list: " + filtered);
// 分组
Map<Integer, List<String>> groupedByLength = list.stream()
.collect(Collectors.groupingBy(String::length));
System.out.println("Grouped by length: " + groupedByLength);
// 求和、平均值等
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream().mapToInt(Integer::intValue).sum();
double average = numbers.stream().mapToInt(Integer::intValue).average().orElse(0);
System.out.println("Sum: " + sum);
System.out.println("Average: " + average);
// 并行流
int parallelSum = numbers.parallelStream().mapToInt(Integer::intValue).sum();
System.out.println("Parallel sum: " + parallelSum);
}
}| 场景 | 推荐集合类型 | 原因 |
|---|---|---|
| 需要存储有序元素,允许重复 | ArrayList | 随机访问效率高 |
| 频繁插入和删除元素 | LinkedList | 插入和删除效率高 |
| 需要存储不重复元素,不关心顺序 | HashSet | 查找效率高 |
| 需要存储不重复元素,保持插入顺序 | LinkedHashSet | 维护插入顺序 |
| 需要存储不重复元素,元素有序 | TreeSet | 元素自动排序 |
| 需要存储键值对,不关心顺序 | HashMap | 查找效率高 |
| 需要存储键值对,保持插入顺序 | LinkedHashMap | 维护插入顺序 |
| 需要存储键值对,键有序 | TreeMap | 键自动排序 |
| 需要队列功能 | LinkedList 或 ArrayDeque | 效率高 |
| 需要优先级队列 | PriorityQueue | 按优先级排序 |
| 需要栈功能 | ArrayDeque | 效率高于 Stack |
| 多线程环境,需要线程安全的 List | CopyOnWriteArrayList | 读多写少性能好 |
| 多线程环境,需要线程安全的 Map | ConcurrentHashMap | 高并发性能好 |
| 集合类型 | 查找 | 插入 | 删除 | 空间占用 |
|---|---|---|---|---|
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) | 高(红黑树) |
PriorityQueue | O(n) | O(log n) | O(log n) | 中 |
时间复杂度:
ArrayList、HashSet、HashMap:大多数操作为 O(1)LinkedList:插入和删除为 O(1),访问为 O(n)TreeSet、TreeMap:大多数操作为 O(log n)空间复杂度:
ArrayList:空间利用率高LinkedList:每个元素需要额外空间存储指针HashSet、HashMap:需要额外空间存储哈希表TreeSet、TreeMap:需要额外空间存储红黑树内存占用:
Trove 库)Arrays.asList() 返回的 List 不能添加或删除元素import java.util.Arrays;
import java.util.List;
public class AsListPitfall {
public static void main(String[] args) {
String[] array = {"A", "B", "C"};
List<String> list = Arrays.asList(array);
// list.add("D"); // 抛出 UnsupportedOperationException
// list.remove(0); // 抛出 UnsupportedOperationException
// 正确做法:创建新的 ArrayList
List<String> mutableList = new ArrayList<>(Arrays.asList(array));
mutableList.add("D"); // 正常
System.out.println(mutableList);
}
}ArrayList 构造时指定容量import java.util.ArrayList;
import java.util.List;
public class ArrayListCapacityExample {
public static void main(String[] args) {
// 如果知道元素数量,建议指定初始容量,避免多次扩容
List<String> list = new ArrayList<>(1000); // 初始容量 1000
// 添加 1000 个元素不会触发扩容
for (int i = 0; i < 1000; i++) {
list.add("Element" + i);
}
}
}subList() 返回的是视图,不是副本import java.util.ArrayList;
import java.util.List;
public class SubListPitfall {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
List<Integer> subList = list.subList(0, 5);
// 修改 subList 会影响原列表
subList.set(0, 100);
System.out.println("Original list: " + list); // 原列表也被修改
// 删除 subList 的元素也会影响原列表
subList.clear();
System.out.println("After clear: " + list); // 原列表的前 5 个元素被删除
// 注意:如果对原列表进行结构性修改(添加/删除),subList 的操作会抛出异常
}
}HashMap 或 HashSet 的键时必须重写 equals() 和 hashCode()import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
class Student {
private String id;
private String name;
public Student(String id, String name) {
this.id = id;
this.name = name;
}
// 必须重写 equals() 和 hashCode()
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(id, student.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
public class CustomKeyPitfall {
public static void main(String[] args) {
Map<Student, Integer> map = new HashMap<>();
Student s1 = new Student("001", "张三");
Student s2 = new Student("001", "张三");
map.put(s1, 100);
System.out.println("Value: " + map.get(s2)); // 如果不重写 equals 和 hashCode,输出 null
}
}== 比较字符串import java.util.HashMap;
import java.util.Map;
public class StringComparisonPitfall {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("key", 1);
String key = new String("key");
System.out.println(map.get(key)); // 输出: 1,因为 HashMap 使用 equals 比较
// 错误:使用 == 比较字符串
String a = "hello";
String b = new String("hello");
System.out.println(a == b); // 输出: false
System.out.println(a.equals(b)); // 输出: true
}
}import java.util.HashMap;
import java.util.Map;
public class WordFrequencyExample {
public static void main(String[] args) {
String text = "hello world hello java world";
// 统计单词频率
Map<String, Integer> frequency = new HashMap<>();
for (String word : text.split(" ")) {
frequency.merge(word, 1, Integer::sum); // Java 8+ 方法
}
System.out.println("Word frequency: " + frequency);
// 输出: {hello=2, world=2, java=1}
// 找出出现次数最多的单词
frequency.entrySet().stream()
.max(Map.Entry.comparingByValue())
.ifPresent(entry -> System.out.println("Most frequent: " + entry.getKey()));
}
}LinkedHashMap 实现 LRU 缓存import java.util.LinkedHashMap;
import java.util.Map;
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75f, true); // accessOrder = true
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > capacity;
}
}
public class LRUCacheExample {
public static void main(String[] args) {
LRUCache<String, Integer> cache = new LRUCache<>(3);
cache.put("A", 1);
cache.put("B", 2);
cache.put("C", 3);
System.out.println("Initial: " + cache);
cache.get("A"); // 访问 A
cache.put("D", 4); // 添加 D,B 被淘汰
System.out.println("After adding D: " + cache); // 输出: {C=3, A=1, D=4}
}
}PriorityQueue 实现任务调度import java.util.PriorityQueue;
import java.util.Queue;
class Task implements Comparable<Task> {
private String name;
private int priority;
public Task(String name, int priority) {
this.name = name;
this.priority = priority;
}
@Override
public int compareTo(Task other) {
return Integer.compare(this.priority, other.priority); // 优先级小的先执行
}
@Override
public String toString() {
return "Task{name='" + name + "', priority=" + priority + "}";
}
}
public class TaskSchedulerExample {
public static void main(String[] args) {
Queue<Task> taskQueue = new PriorityQueue<>();
taskQueue.offer(new Task("Low priority task", 3));
taskQueue.offer(new Task("High priority task", 1));
taskQueue.offer(new Task("Medium priority task", 2));
// 按优先级执行任务
while (!taskQueue.isEmpty()) {
System.out.println("Executing: " + taskQueue.poll());
}
// 输出顺序: High priority task -> Medium priority task -> Low priority task
}
}ConcurrentHashMap 实现线程安全的计数器import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
class Counter {
private ConcurrentMap<String, Integer> counters = new ConcurrentHashMap<>();
public void increment(String key) {
counters.merge(key, 1, Integer::sum);
}
public int get(String key) {
return counters.getOrDefault(key, 0);
}
public void printAll() {
counters.forEach((k, v) -> System.out.println(k + ": " + v));
}
}
public class CounterExample {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
// 模拟多线程环境
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
counter.increment("key");
}
};
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(task);
threads[i].start();
}
for (Thread thread : threads) {
thread.join();
}
counter.printAll(); // 输出: key: 10000
}
}ArrayList 和 LinkedList 的区别| 特性 | ArrayList | LinkedList |
|---|---|---|
| 底层实现 | 动态数组 | 双向链表 |
| 随机访问 | O(1) | O(n) |
| 插入删除 | O(n)(需要移动元素) | O(1)(只需修改指针) |
| 内存占用 | 较低 | 较高(存储指针) |
| 缓存友好性 | 好 | 差 |
| 适用场景 | 频繁查询、随机访问 | 频繁插入、删除 |
HashMap 的底层实现原理JDK 1.8 中 HashMap 的底层实现:
hash(key) ^ (hash(key) >>> 16)(n - 1) & hashConcurrentHashMap 的实现原理JDK 1.7:
ReentrantLockJDK 1.8:
synchronizedHashSet 如何保证元素不重复hashCode() 方法计算哈希值equals() 方法比较equals() 返回 true,则认为是重复元素,不添加equals() 必须重写 hashCode()因为如果两个对象 equals() 返回 true,它们的 hashCode() 必须相同。如果不重写 hashCode(),可能导致两个相等的对象哈希值不同,从而在 HashSet 或 HashMap 中出现重复。
fail-fast 和 fail-safe 的区别ConcurrentModificationException。如 ArrayList、HashMap 的迭代器。CopyOnWriteArrayList 的迭代器(基于快照副本);ConcurrentHashMap 的迭代器为弱一致性(不抛异常,但不保证反映遍历开始后的修改)。ArrayList 线程安全Collections.synchronizedList()CopyOnWriteArrayListsynchronized 关键字手动同步TreeMap 和 HashMap 的区别| 特性 | HashMap | TreeMap |
|---|---|---|
| 底层实现 | 哈希表 | 红黑树 |
| 元素顺序 | 无序 | 有序(按键排序) |
| 查找性能 | O(1) | O(log n) |
| null 键 | 允许一个 | 不允许 |
| 适用场景 | 需要快速查找 | 需要键有序 |
PriorityQueue 的实现原理PriorityBlockingQueue根据需求选择:
HashMap、HashSetTreeMap、TreeSet、LinkedHashMap、LinkedHashSetLinkedList、ArrayDeque、PriorityQueueConcurrentHashMap、CopyOnWriteArrayListLinkedListArrayListJava 集合框架提供了丰富的数据结构,用于存储和操作对象集合。选择合适的集合类型对于编写高效、可维护的代码至关重要。
List 集合:
ArrayList 适合随机访问,LinkedList 适合频繁插入和删除CopyOnWriteArrayList 适合读多写少的并发场景Queue 队列:
LinkedList 可作为队列使用,ArrayDeque 性能更好PriorityQueue 按优先级排序Set 集合:
HashSet 提供快速查找,LinkedHashSet 维护插入顺序,TreeSet 维护排序顺序equals() 和 hashCode() 方法Map 集合:
HashMap 提供快速查找,LinkedHashMap 维护插入顺序,TreeMap 维护排序顺序equals() 和 hashCode() 方法迭代器:
remove() 方法安全删除元素线程安全:
ConcurrentHashMap)或同步包装器List、Set、Map)声明变量,而不是具体实现类equals() 和 hashCode() 方法通过掌握这些集合类的特性和使用方法,可以编写出更加高效、灵活和可维护的 Java 代码。
| 特性 | 旧版(Java 8) | Java 9/10/16/21 |
|---|---|---|
| 不可变集合工厂 | Collections.unmodifiableXxx 包装可变集合 | List.of/Set.of/Map.of(Java 9)、List.copyOf(Java 10)真正不可变 |
| 迭代删除 | 手动 Iterator.remove() | Collection.removeIf(Java 8) |
| 空集合 | Collections.emptyList() | List.of() 等工厂方法(Java 9) |
| 顺序遍历 | forEach(Java 8) | Stream.toList()(Java 16)不可变收集 |
| 序列化安全 | 普通集合 | record 天然不可变(Java 16+),集合中承载 record 更安全 |