Java集合框架全面详解

1. 集合框架概述

1.1 什么是集合框架?

集合框架是一个统一的架构,用于表示和操作集合,使集合能够独立于其实现细节进行操作。Java集合框架提供了一系列接口和类,用于存储、检索、操作和传输数据对象。

1.2 为什么需要集合框架?

在Java早期版本中,Java提供了有限的几个类用于数据存储和操作(如Vector、Hashtable等),但这些类缺乏统一的设计理念。Java 1.2引入集合框架,目的是:

提供高性能、高质量的数据结构和算法实现减少编程工作量,通过提供现成的数据结构提供接口和实现分离的设计,使代码更具可重用性和互操作性建立通用语言,方便开发者交流算法和数据结构

1.3 集合框架的组成部分

Java集合框架主要由三部分组成:

接口(Interfaces):表示集合的抽象数据类型。允许集合独立于其实现细节进行操作。实现类(Implementations):接口的具体实现,即可重用的数据结构。算法(Algorithms):对实现了集合接口的对象进行操作的方法,如搜索和排序。

2. 核心接口

Java集合框架的核心接口构成了框架的基础。以下是最重要的接口:

2.1 Collection接口

Collection是集合层次结构的根接口,所有集合类都实现了这个接口。它提供了适用于所有集合的基本操作。

主要方法:

boolean add(E e) - 添加元素boolean remove(Object o) - 移除元素boolean contains(Object o) - 检查是否包含指定元素int size() - 返回集合中的元素数量boolean isEmpty() - 检查集合是否为空Iterator iterator() - 返回迭代器void clear() - 清空集合boolean containsAll(Collection c) - 检查是否包含另一个集合的所有元素boolean addAll(Collection c) - 添加另一个集合的所有元素boolean removeAll(Collection c) - 移除同时存在于指定集合中的所有元素boolean retainAll(Collection c) - 仅保留同时存在于指定集合中的元素Object[] toArray() - 返回包含集合中所有元素的数组

2.2 List接口

List是一个有序的Collection,用户可以通过索引访问元素,并且允许重复元素。

特点:

有序(插入顺序)允许重复元素可以通过索引访问元素

额外的主要方法:

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) - 返回指定元素最后一次出现的索引List subList(int fromIndex, int toIndex) - 返回指定范围内的子列表

2.3 Set接口

Set是一个不包含重复元素的Collection。

特点:

不允许重复元素大多数实现类不保证元素的顺序有些实现类可能保证特定的顺序(如LinkedHashSet维护插入顺序)

Set接口没有在Collection接口之外添加新方法,只是改变了一些方法的行为语义。例如,add方法对于已存在的元素将返回false。

2.4 Queue接口

Queue代表了先进先出(FIFO)的数据结构,除了基本的Collection操作外,还提供了额外的插入、提取和检查操作。

主要方法:

boolean offer(E e) - 将元素添加到队列(不抛出异常)E poll() - 获取并移除队首元素,如果队列为空则返回nullE peek() - 获取但不移除队首元素,如果队列为空则返回nullE element() - 获取但不移除队首元素,如果队列为空则抛出异常E remove() - 获取并移除队首元素,如果队列为空则抛出异常

2.5 Deque接口

Deque(双端队列)是Queue的子接口,允许在两端进行插入和删除操作。

主要方法:

void addFirst(E e) / void addLast(E e) - 在队列前端/后端添加元素E removeFirst() / E removeLast() - 移除并返回队列前端/后端的元素E getFirst() / E getLast() - 获取但不移除队列前端/后端的元素boolean offerFirst(E e) / boolean offerLast(E e) - 在队列前端/后端添加元素(不抛出异常)E pollFirst() / E pollLast() - 获取并移除队列前端/后端的元素,如果队列为空则返回nullE peekFirst() / E peekLast() - 获取但不移除队列前端/后端的元素,如果队列为空则返回null

2.6 Map接口

Map虽然不是Collection的子接口,但也是集合框架的一部分。Map将键映射到值,不能包含重复的键,每个键最多映射到一个值。

主要方法:

V put(K key, V value) - 添加键值对V get(Object key) - 获取指定键对应的值V remove(Object key) - 移除指定键对应的值boolean containsKey(Object key) - 检查是否包含指定键boolean containsValue(Object value) - 检查是否包含指定值Set keySet() - 返回包含所有键的SetCollection values() - 返回包含所有值的CollectionSet> entrySet() - 返回包含所有键值对的Setint size() - 返回键值对的数量boolean isEmpty() - 检查Map是否为空void clear() - 清空Map

3. 实现类

Java集合框架提供了许多实现类,每个类都有其特定的特性和适用场景。

3.1 List实现

3.1.1 ArrayList

ArrayList是基于数组实现的List,提供了快速的随机访问,但插入和删除元素的速度较慢。

特点:

基于动态数组实现随机访问元素的时间复杂度为O(1)在末尾添加元素的平均时间复杂度为O(1)在中间插入/删除元素的时间复杂度为O(n)非同步(线程不安全)

示例:

List arrayList = new ArrayList<>();

arrayList.add("Java");

arrayList.add("Python");

arrayList.add("C++");

System.out.println(arrayList.get(1)); // 输出: Python

3.1.2 LinkedList

LinkedList是基于双向链表实现的List,提供了快速的插入和删除操作,但随机访问元素的速度较慢。

特点:

基于双向链表实现随机访问元素的时间复杂度为O(n)插入/删除元素的时间复杂度为O(1)(假设已知位置)同时实现了List和Deque接口非同步(线程不安全)

示例:

LinkedList linkedList = new LinkedList<>();

linkedList.add("Java");

linkedList.add("Python");

linkedList.addFirst("C++"); // 在开头添加元素

linkedList.addLast("JavaScript"); // 在末尾添加元素

System.out.println(linkedList); // 输出: [C++, Java, Python, JavaScript]

3.1.3 Vector

Vector是早期Java版本中的集合类,现在已经被重新设计为实现List接口。它类似于ArrayList,但所有方法都是同步的。

特点:

基于动态数组实现所有方法都是同步的(线程安全)性能比ArrayList稍差现在一般不推荐使用,除非需要线程安全

示例:

Vector vector = new Vector<>();

vector.add("Java");

vector.add("Python");

vector.add("C++");

System.out.println(vector.elementAt(1)); // 输出: Python

3.1.4 Stack

Stack继承自Vector,实现了标准的后进先出(LIFO)栈。

特点:

继承自Vector,所以是线程安全的表示后进先出(LIFO)的数据结构现在通常推荐使用Deque接口的实现类代替Stack

主要方法:

E push(E item) - 将元素压入栈顶E pop() - 移除并返回栈顶元素E peek() - 获取但不移除栈顶元素boolean empty() - 检查栈是否为空int search(Object o) - 返回元素在栈中的位置

示例:

Stack stack = new Stack<>();

stack.push("Java");

stack.push("Python");

stack.push("C++");

System.out.println(stack.pop()); // 输出: C++

System.out.println(stack.peek()); // 输出: Python

3.2 Set实现

3.2.1 HashSet

HashSet是基于哈希表实现的Set,它不保证集合的迭代顺序,允许使用null元素。

特点:

基于HashMap实现不保证元素的顺序允许null元素提供最佳的性能非同步(线程不安全)

示例:

Set hashSet = new HashSet<>();

hashSet.add("Java");

hashSet.add("Python");

hashSet.add("Java"); // 重复元素,不会被添加

System.out.println(hashSet); // 输出顺序可能不同

3.2.2 LinkedHashSet

LinkedHashSet是HashSet的子类,它维护了一个双向链表,记录了元素的插入顺序。

特点:

基于LinkedHashMap实现维护元素的插入顺序允许null元素性能略低于HashSet非同步(线程不安全)

示例:

Set linkedHashSet = new LinkedHashSet<>();

linkedHashSet.add("Java");

linkedHashSet.add("Python");

linkedHashSet.add("C++");

System.out.println(linkedHashSet); // 输出: [Java, Python, C++]

3.2.3 TreeSet

TreeSet是基于红黑树实现的NavigableSet,它按照元素的自然顺序或者指定的比较器排序。

特点:

基于TreeMap实现元素按照自然顺序或指定的比较器排序不允许null元素提供了很多额外的方法来处理有序集合性能比HashSet和LinkedHashSet差非同步(线程不安全)

示例:

TreeSet treeSet = new TreeSet<>();

treeSet.add("Java");

treeSet.add("Python");

treeSet.add("C++");

System.out.println(treeSet); // 输出: [C++, Java, Python](按字母顺序)

// 使用比较器

TreeSet customTreeSet = new TreeSet<>(Comparator.reverseOrder());

customTreeSet.add("Java");

customTreeSet.add("Python");

customTreeSet.add("C++");

System.out.println(customTreeSet); // 输出: [Python, Java, C++](按字母逆序)

3.3 Queue实现

3.3.1 PriorityQueue

PriorityQueue是基于优先级堆实现的队列,元素按照自然顺序或者指定的比较器排序。

特点:

基于优先级堆(通常是二叉堆)实现元素按照自然顺序或指定的比较器排序不允许null元素非同步(线程不安全)

示例:

PriorityQueue priorityQueue = new PriorityQueue<>();

priorityQueue.add(5);

priorityQueue.add(1);

priorityQueue.add(3);

System.out.println(priorityQueue.poll()); // 输出: 1

System.out.println(priorityQueue.poll()); // 输出: 3

3.3.2 ArrayDeque

ArrayDeque是基于可调整大小的数组实现的双端队列,它作为栈和队列都比Stack和LinkedList更高效。

特点:

基于循环数组实现没有容量限制不允许null元素作为栈使用时比Stack更快作为队列使用时比LinkedList更快非同步(线程不安全)

示例:

// 作为队列使用

Queue queue = new ArrayDeque<>();

queue.offer("Java");

queue.offer("Python");

queue.offer("C++");

System.out.println(queue.poll()); // 输出: Java

// 作为栈使用

Deque stack = new ArrayDeque<>();

stack.push("Java");

stack.push("Python");

stack.push("C++");

System.out.println(stack.pop()); // 输出: C++

3.4 Map实现

3.4.1 HashMap

HashMap是基于哈希表实现的Map,它不保证映射的顺序,允许使用null键和null值。

特点:

基于哈希表实现不保证映射的顺序允许一个null键和多个null值提供最佳的性能非同步(线程不安全)

示例:

Map hashMap = new HashMap<>();

hashMap.put("Java", 1995);

hashMap.put("Python", 1991);

hashMap.put("C++", 1983);

System.out.println(hashMap.get("Java")); // 输出: 1995

3.4.2 LinkedHashMap

LinkedHashMap是HashMap的子类,它维护了一个双向链表,可以记录元素的插入顺序或访问顺序。

特点:

基于HashMap和双向链表实现维护元素的插入顺序或访问顺序(可配置)允许一个null键和多个null值性能略低于HashMap非同步(线程不安全)

示例:

// 按插入顺序

Map linkedHashMap = new LinkedHashMap<>();

linkedHashMap.put("Java", 1995);

linkedHashMap.put("Python", 1991);

linkedHashMap.put("C++", 1983);

System.out.println(linkedHashMap); // 输出保持插入顺序

// 按访问顺序(LRU缓存)

Map lruCache = new LinkedHashMap<>(16, 0.75f, true);

lruCache.put("Java", 1995);

lruCache.put("Python", 1991);

lruCache.put("C++", 1983);

lruCache.get("Java"); // 访问Java

System.out.println(lruCache); // Java将移到最后(最近访问)

3.4.3 TreeMap

TreeMap是基于红黑树实现的NavigableMap,它按照键的自然顺序或者指定的比较器排序。

特点:

基于红黑树实现键按照自然顺序或指定的比较器排序不允许null键,但允许多个null值提供了很多额外的方法来处理有序映射性能比HashMap和LinkedHashMap差非同步(线程不安全)

示例:

TreeMap treeMap = new TreeMap<>();

treeMap.put("Java", 1995);

treeMap.put("Python", 1991);

treeMap.put("C++", 1983);

System.out.println(treeMap); // 输出按键的字母顺序排序

// 使用比较器

TreeMap customTreeMap = new TreeMap<>(Comparator.reverseOrder());

customTreeMap.put("Java", 1995);

customTreeMap.put("Python", 1991);

customTreeMap.put("C++", 1983);

System.out.println(customTreeMap); // 输出按键的字母逆序排序

3.4.4 Hashtable

Hashtable是早期Java版本中的类,现在已经被重新设计为实现Map接口。它类似于HashMap,但所有方法都是同步的,并且不允许null键或值。

特点:

基于哈希表实现所有方法都是同步的(线程安全)不允许null键或值性能比HashMap差现在一般不推荐使用,除非需要线程安全

示例:

Hashtable hashtable = new Hashtable<>();

hashtable.put("Java", 1995);

hashtable.put("Python", 1991);

hashtable.put("C++", 1983);

System.out.println(hashtable.get("Java")); // 输出: 1995

3.4.5 Properties

Properties是Hashtable的子类,用于存储字符串键值对。它通常用于读取和写入配置文件。

特点:

继承自Hashtable,所以是线程安全的键和值都是字符串可以从流中加载和保存提供了默认值的功能

示例:

Properties properties = new Properties();

properties.setProperty("username", "admin");

properties.setProperty("password", "123456");

System.out.println(properties.getProperty("username")); // 输出: admin

System.out.println(properties.getProperty("email", "default@example.com")); // 输出默认值: default@example.com

// 保存到文件

try (FileOutputStream out = new FileOutputStream("config.properties")) {

properties.store(out, "Configuration");

} catch (IOException e) {

e.printStackTrace();

}

// 从文件加载

Properties loadedProps = new Properties();

try (FileInputStream in = new FileInputStream("config.properties")) {

loadedProps.load(in);

System.out.println(loadedProps.getProperty("username")); // 输出: admin

} catch (IOException e) {

e.printStackTrace();

}

4. 工具类

Java集合框架提供了两个主要的工具类,用于对集合和数组进行操作。

4.1 Collections类

Collections类提供了一系列静态方法,用于操作和返回集合。

主要方法:

排序

sort(List list) - 使用自然顺序对列表进行排序sort(List list, Comparator c) - 使用比较器对列表进行排序 查找

binarySearch(List> list, T key) - 使用二分查找算法查找元素max(Collection coll) - 返回最大元素min(Collection coll) - 返回最小元素frequency(Collection c, Object o) - 返回指定元素在集合中出现的次数 修改

reverse(List list) - 反转列表shuffle(List list) - 随机打乱列表swap(List list, int i, int j) - 交换列表中的两个元素fill(List list, T obj) - 用指定元素替换列表中的所有元素copy(List dest, List src) - 复制列表rotate(List list, int distance) - 旋转列表replaceAll(List list, T oldVal, T newVal) - 替换列表中所有的指定元素 特殊集合

unmodifiableXXX(XXX c) - 返回不可修改的视图synchronizedXXX(XXX c) - 返回同步的视图checkedXXX(XXX c, Class type) - 返回类型检查的视图emptyXXX() - 返回空集合singletonXXX(T o) - 返回只包含一个元素的集合

示例:

List numbers = new ArrayList<>();

numbers.add(3);

numbers.add(1);

numbers.add(2);

// 排序

Collections.sort(numbers);

System.out.println(numbers); // 输出: [1, 2, 3]

// 二分查找

int index = Collections.binarySearch(numbers, 2);

System.out.println(index); // 输出: 1

// 反转

Collections.reverse(numbers);

System.out.println(numbers); // 输出: [3, 2, 1]

// 打乱

Collections.shuffle(numbers);

System.out.println(numbers); // 输出随机顺序

// 不可修改的集合

List unmodifiableList = Collections.unmodifiableList(numbers);

try {

unmodifiableList.add(4); // 抛出UnsupportedOperationException

} catch (UnsupportedOperationException e) {

System.out.println("Cannot modify unmodifiable list");

}

// 同步集合

List synchronizedList = Collections.synchronizedList(numbers);

// 单例集合

Set singletonSet = Collections.singleton(1);

4.2 Arrays类

Arrays类提供了一系列静态方法,用于操作数组。

主要方法:

排序

sort(T[] a) - 使用自然顺序对数组进行排序sort(T[] a, Comparator c) - 使用比较器对数组进行排序parallelSort(T[] a) - 使用并行排序算法对数组进行排序 查找

binarySearch(T[] a, T key) - 使用二分查找算法查找元素 比较和填充

equals(T[] a, T[] a2) - 比较两个数组是否相等fill(T[] a, T val) - 用指定值填充数组copyOf(T[] original, int newLength) - 复制数组copyOfRange(T[] original, int from, int to) - 复制数组的指定范围 转换

asList(T... a) - 返回由指定数组支持的固定大小的列表stream(T[] array) - 返回由数组支持的顺序Stream 其他

toString(T[] a) - 返回数组的字符串表示形式deepToString(Object[] a) - 返回多维数组的字符串表示形式hashCode(T[] a) - 返回数组的哈希码deepHashCode(Object[] a) - 返回多维数组的哈希码setAll(T[] array, IntFunction generator) - 使用生成器函数设置数组的所有元素

示例:

Integer[] numbers = {3, 1, 2};

// 排序

Arrays.sort(numbers);

System.out.println(Arrays.toString(numbers)); // 输出: [1, 2, 3]

// 二分查找

int index = Arrays.binarySearch(numbers, 2);

System.out.println(index); // 输出: 1

// 填充

Arrays.fill(numbers, 0);

System.out.println(Arrays.toString(numbers)); // 输出: [0, 0, 0]

// 复制

Integer[] original = {1, 2, 3};

Integer[] copy = Arrays.copyOf(original, 5);

System.out.println(Arrays.toString(copy)); // 输出: [1, 2, 3, null, null]

// 转换为List

List list = Arrays.asList(1, 2, 3);

System.out.println(list); // 输出: [1, 2, 3]

// 注意: 返回的列表是固定大小的,不能添加或删除元素

// 多维数组

Integer[][] matrix = {{1, 2}, {3, 4}};

System.out.println(Arrays.deepToString(matrix)); // 输出: [[1, 2], [3, 4]]

5. 特殊集合

5.1 Legacy集合

Legacy集合是Java 1.2之前就存在的集合类,它们已经被重新设计为适应集合框架。

包括:

Vector - 线程安全的动态数组Stack - 继承自Vector的LIFO栈Hashtable - 线程安全的哈希表Properties - 继承自Hashtable的字符串键值对存储Enumeration - 元素枚举接口,类似于Iterator

尽管这些类现在都是集合框架的一部分,但它们的设计与框架中的其他部分不太协调。一般情况下,应该使用它们的替代品(ArrayList、HashMap等)。

5.2 并发集合

Java提供了许多线程安全的集合实现,它们位于java.util.concurrent包中。

主要类:

ConcurrentHashMap - 高并发、高性能的线程安全HashMapCopyOnWriteArrayList - 线程安全的ArrayList,适用于读多写少的场景CopyOnWriteArraySet - 使用CopyOnWriteArrayList实现的线程安全SetConcurrentLinkedQueue - 线程安全的非阻塞队列ConcurrentLinkedDeque - 线程安全的非阻塞双端队列ConcurrentSkipListMap - 线程安全的NavigableMapConcurrentSkipListSet - 线程安全的NavigableSetArrayBlockingQueue - 基于数组的有界阻塞队列LinkedBlockingQueue - 基于链表的可选有界阻塞队列PriorityBlockingQueue - 支持优先级的无界阻塞队列DelayQueue - 延迟元素的无界阻塞队列LinkedTransferQueue - 基于链表的无界传输队列LinkedBlockingDeque - 基于链表的可选有界阻塞双端队列SynchronousQueue - 没有内部容量的阻塞队列

示例:

// ConcurrentHashMap

Map concurrentMap = new ConcurrentHashMap<>();

concurrentMap.put("Java", 1995);

concurrentMap.put("Python", 1991);

System.out.println(concurrentMap.get("Java")); // 输出: 1995

// CopyOnWriteArrayList

List copyOnWriteList = new CopyOnWriteArrayList<>();

copyOnWriteList.add("Java");

copyOnWriteList.add("Python");

// 适合多线程读操作多,写操作少的场景

// ArrayBlockingQueue

BlockingQueue blockingQueue = new ArrayBlockingQueue<>(10); // 容量为10

blockingQueue.put("Java"); // 如果队列已满,put方法会阻塞

String item = blockingQueue.take(); // 如果队列为空,take方法会阻塞

System.out.println(item); // 输出: Java

5.3 不可变集合

不可变集合是指创建后不能修改的集合。Java提供了几种方式来创建不可变集合:

Collections工具类

Collections.unmodifiableXXX(XXX c) - 返回不可修改的视图 List.of()、Set.of()和Map.of()方法(Java 9+)

创建不可变的集合 Guava库的ImmutableXXX类

提供了更丰富的不可变集合操作

示例:

// 使用Collections

List modifiableList = new ArrayList<>();

modifiableList.add("Java");

modifiableList.add("Python");

List unmodifiableList = Collections.unmodifiableList(modifiableList);

// 使用Java 9+的工厂方法

List immutableList = List.of("Java", "Python", "C++");

Set immutableSet = Set.of("Java", "Python", "C++");

Map immutableMap = Map.of("Java", 1995, "Python", 1991, "C++", 1983);

// 尝试修改不可变集合会抛出UnsupportedOperationException

try {

immutableList.add("Go");

} catch (UnsupportedOperationException e) {

System.out.println("Cannot modify immutable list");

}

6. 集合操作

6.1 遍历集合

Java提供了多种方式来遍历集合:

使用Iterator

List list = new ArrayList<>();

list.add("Java");

list.add("Python");

list.add("C++");

Iterator iterator = list.iterator();

while (iterator.hasNext()) {

String element = iterator.next();

System.out.println(element);

// 安全地删除元素

if (element.equals("Python")) {

iterator.remove();

}

}

使用for-each循环

for (String element : list) {

System.out.println(element);

// 注意: 在for-each循环中不能安全地修改集合

}

使用索引(仅适用于List)

for (int i = 0; i < list.size(); i++) {

System.out.println(list.get(i));

}

使用forEach方法(Java 8+)

list.forEach(element -> System.out.println(element));

// 或使用方法引用

list.forEach(System.out::println);

使用Stream API(Java 8+)

list.stream()

.filter(element -> element.startsWith("J"))

.forEach(System.out::println);

6.2 排序集合

使用Collections.sort()

List list = new ArrayList<>();

list.add("C++");

list.add("Python");

list.add("Java");

// 自然顺序排序

Collections.sort(list);

System.out.println(list); // 输出: [C++, Java, Python]

// 使用比较器排序

Collections.sort(list, Comparator.reverseOrder());

System.out.println(list); // 输出: [Python, Java, C++]

使用List.sort()(Java 8+)

// 自然顺序排序

list.sort(null);

System.out.println(list); // 输出: [C++, Java, Python]

// 使用比较器排序

list.sort(Comparator.reverseOrder());

System.out.println(list); // 输出: [Python, Java, C++]

使用TreeSet或TreeMap自动排序

Set treeSet = new TreeSet<>();

treeSet.add("C++");

treeSet.add("Python");

treeSet.add("Java");

System.out.println(treeSet); // 输出: [C++, Java, Python]

使用Stream API排序(Java 8+)

List sortedList = list.stream()

.sorted()

.collect(Collectors.toList());

System.out.println(sortedList); // 输出: [C++, Java, Python]

6.3 集合间的转换

Collection转数组

List list = new ArrayList<>();

list.add("Java");

list.add("Python");

// 转换为Object数组

Object[] objectArray = list.toArray();

// 转换为指定类型的数组

String[] stringArray = list.toArray(new String[0]);

// 或使用Java 11+的新方法

String[] stringArray2 = list.toArray(String[]::new);

数组转Collection

String[] array = {"Java", "Python", "C++"};

// 使用Arrays.asList() - 返回固定大小的List

List list = Arrays.asList(array);

// 使用new ArrayList<>(Arrays.asList()) - 返回可调整大小的List

List modifiableList = new ArrayList<>(Arrays.asList(array));

// 使用List.of() - 返回不可变List(Java 9+)

List immutableList = List.of(array);

// 使用Stream API(Java 8+)

List streamList = Arrays.stream(array).collect(Collectors.toList());

Set streamSet = Arrays.stream(array).collect(Collectors.toSet());

Collection之间的转换

List list = new ArrayList<>();

list.add("Java");

list.add("Python");

list.add("Java"); // 重复元素

// List转Set(移除重复元素)

Set set = new HashSet<>(list);

System.out.println(set); // 输出: [Java, Python]

// Set转List

List newList = new ArrayList<>(set);

// 使用Stream API

List streamList = set.stream().collect(Collectors.toList());

Set streamSet = list.stream().collect(Collectors.toSet());

Map与Collection的转换

Map map = new HashMap<>();

map.put("Java", 1995);

map.put("Python", 1991);

// 获取键的集合

Set keys = map.keySet();

// 获取值的集合

Collection values = map.values();

// 获取键值对的集合

Set> entries = map.entrySet();

// Collection转Map(使用Stream API)

List list = Arrays.asList("Java", "Python", "C++");

Map lengthMap = list.stream()

.collect(Collectors.toMap(

s -> s, // 键映射函数

String::length // 值映射函数

));

System.out.println(lengthMap); // 输出: {Java=4, C++=3, Python=6}

7. 高级主题

7.1 泛型与集合

Java泛型允许在编译时提供类型安全性,使集合更加类型安全。

示例:

// 不使用泛型(Java 5之前)

List list = new ArrayList();

list.add("Java");

list.add(1); // 可以添加任何类型的对象

String s = (String) list.get(0); // 需要强制类型转换

Integer i = (Integer) list.get(1); // 需要强制类型转换

// 使用泛型(Java 5+)

List stringList = new ArrayList<>();

stringList.add("Java");

// stringList.add(1); // 编译错误,只能添加String类型

String s2 = stringList.get(0); // 不需要强制类型转换

泛型通配符:

- 无界通配符,表示任何类型 - 上界通配符,表示T或T的子类型 - 下界通配符,表示T或T的超类型

示例:

// 使用上界通配符

List numbers = new ArrayList(); // 合法

// numbers.add(1); // 不合法,不能添加元素

Number n = numbers.get(0); // 合法,可以获取元素

// 使用下界通配符

List integers = new ArrayList(); // 合法

integers.add(1); // 合法,可以添加Integer

// Integer i = integers.get(0); // 不合法,不能直接获取

Object o = integers.get(0); // 合法,可以获取为Object

PECS原则(Producer Extends, Consumer Super):

如果你只需要从集合中获取元素(生产者),使用? extends T如果你只需要向集合中添加元素(消费者),使用? super T

7.2.1 Comparable接口

Comparable接口允许类的对象进行自然排序。实现这个接口的类必须提供compareTo方法。

public class Student implements Comparable {

private String name;

private int age;

public Student(String name, int age) {

this.name = name;

this.age = age;

}

@Override

public int compareTo(Student other) {

// 按年龄升序排序

return this.age - other.age;

}

@Override

public String toString() {

return name + " (" + age + ")";

}

}

// 使用

List students = new ArrayList<>();

students.add(new Student("Alice", 22));

students.add(new Student("Bob", 20));

students.add(new Student("Charlie", 25));

Collections.sort(students); // 使用自然顺序排序

System.out.println(students); // 按年龄升序输出

7.2.2 Comparator接口

Comparator接口提供了一种外部比较策略,允许在不修改类的情况下定义多种排序方式。

public class Student {

private String name;

private int age;

public Student(String name, int age) {

this.name = name;

this.age = age;

}

public String getName() {

return name;

}

public int getAge() {

return age;

}

@Override

public String toString() {

return name + " (" + age + ")";

}

}

// 使用Comparator

List students = new ArrayList<>();

students.add(new Student("Alice", 22));

students.add(new Student("Bob", 20));

students.add(new Student("Charlie", 25));

// 按年龄排序

Collections.sort(students, new Comparator() {

@Override

public int compare(Student s1, Student s2) {

return s1.getAge() - s2.getAge();

}

});

System.out.println(students); // 按年龄升序输出

// 使用Lambda表达式(Java 8+)

Collections.sort(students, (s1, s2) -> s1.getAge() - s2.getAge());

// 使用Comparator静态方法(Java 8+)

Collections.sort(students, Comparator.comparingInt(Student::getAge));

// 按姓名排序

Collections.sort(students, Comparator.comparing(Student::getName));

System.out.println(students); // 按姓名升序输出

// 组合比较器

Comparator byAgeDesc = Comparator.comparingInt(Student::getAge).reversed();

Comparator byNameThenAgeDesc = Comparator.comparing(Student::getName)

.thenComparing(byAgeDesc);

students.sort(byNameThenAgeDesc);

7.3 自定义集合实现

尽管Java集合框架提供了丰富的实现,但有时您可能需要创建自定义的集合类。

创建自定义集合的方式:

扩展现有集合类

public class LoggingArrayList extends ArrayList {

private static final long serialVersionUID = 1L;

@Override

public boolean add(E e) {

System.out.println("Adding element: " + e);

return super.add(e);

}

@Override

public E remove(int index) {

E element = super.remove(index);

System.out.println("Removed element at index " + index + ": " + element);

return element;

}

}

实现集合接口

public class SimpleArrayList implements List {

private Object[] elements;

private int size;

public SimpleArrayList() {

elements = new Object[10];

size = 0;

}

@Override

public boolean add(E e) {

ensureCapacity();

elements[size++] = e;

return true;

}

@SuppressWarnings("unchecked")

@Override

public E get(int index) {

checkIndex(index);

return (E) elements[index];

}

@Override

public int size() {

return size;

}

// 省略其他必要的方法实现...

private void ensureCapacity() {

if (size == elements.length) {

elements = Arrays.copyOf(elements, size * 2);

}

}

private void checkIndex(int index) {

if (index < 0 || index >= size) {

throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);

}

}

}

使用组合而非继承

public class CountingSet implements Set {

private final Set delegate;

private int addCount = 0;

public CountingSet(Set delegate) {

this.delegate = delegate;

}

@Override

public boolean add(E e) {

addCount++;

return delegate.add(e);

}

@Override

public boolean addAll(Collection c) {

addCount += c.size();

return delegate.addAll(c);

}

public int getAddCount() {

return addCount;

}

// 委托其他方法到delegate

@Override

public int size() {

return delegate.size();

}

// 省略其他委托方法...

}

8. 最佳实践与常见陷阱

8.1 选择合适的集合

选择集合时应考虑的因素:

需要存储的元素类型访问模式(随机访问 vs. 顺序访问)是否需要保持顺序是否允许重复元素是否需要线程安全性能需求

常见集合的选择指南:

需要快速随机访问:ArrayList频繁在中间插入/删除元素:LinkedList不允许重复元素,没有特定顺序要求:HashSet不允许重复元素,需要维护插入顺序:LinkedHashSet不允许重复元素,需要元素保持排序:TreeSet需要键值对映射,没有特定顺序要求:HashMap需要键值对映射,需要维护插入顺序:LinkedHashMap需要键值对映射,需要键保持排序:TreeMap需要FIFO队列:LinkedList或ArrayDeque需要LIFO栈:ArrayDeque需要优先级队列:PriorityQueue需要线程安全:考虑java.util.concurrent包中的集合

8.2 性能考虑

各种集合操作的时间复杂度:

集合类型添加删除获取包含迭代ArrayListO(1)*O(n)O(1)O(n)O(n)LinkedListO(1)O(1)*O(n)O(n)O(n)HashSetO(1)O(1)N/AO(1)O(n)LinkedHashSetO(1)O(1)N/AO(1)O(n)TreeSetO(log n)O(log n)N/AO(log n)O(n)HashMapO(1)O(1)O(1)O(1)O(n)LinkedHashMapO(1)O(1)O(1)O(1)O(n)TreeMapO(log n)O(log n)O(log n)O(log n)O(n)

*:平均情况,最坏情况可能是O(n)

性能优化技巧:

为集合预分配容量,避免频繁扩容使用适当的初始容量和负载因子优先使用批量操作(如addAll)避免在for-each循环中删除元素在合适的场景使用并行流

8.3 常见陷阱

并发修改异常

在迭代过程中修改集合可能会导致ConcurrentModificationException:

List list = new ArrayList<>();

list.add("Java");

list.add("Python");

list.add("C++");

// 错误方式:可能抛出ConcurrentModificationException

for (String language : list) {

if (language.equals("Python")) {

list.remove(language);

}

}

// 正确方式1:使用Iterator的remove方法

Iterator iterator = list.iterator();

while (iterator.hasNext()) {

String language = iterator.next();

if (language.equals("Python")) {

iterator.remove();

}

}

// 正确方式2:使用removeIf方法(Java 8+)

list.removeIf(language -> language.equals("Python"));

Arrays.asList()的固定大小

Arrays.asList()返回的列表是固定大小的,不能添加或删除元素:

String[] array = {"Java", "Python", "C++"};

List list = Arrays.asList(array);

try {

list.add("Go"); // 抛出UnsupportedOperationException

} catch (UnsupportedOperationException e) {

System.out.println("Cannot add to fixed-size list");

}

// 解决方案:转换为ArrayList

List modifiableList = new ArrayList<>(Arrays.asList(array));

modifiableList.add("Go"); // 正常工作

Map.keySet()和values()返回的视图

Map.keySet()和values()返回的集合是Map的视图,修改这些集合会影响原始Map:

Map map = new HashMap<>();

map.put("Java", 1995);

map.put("Python", 1991);

Set keys = map.keySet();

keys.remove("Java"); // 同时从map中移除Java键值对

System.out.println(map); // 输出: {Python=1991}

HashSet和HashMap的hashCode()和equals()合约

当使用自定义类作为HashSet的元素或HashMap的键时,必须正确实现hashCode()和equals()方法:

public class Person {

private String name;

private int age;

// 构造函数、getter和setter省略

@Override

public boolean equals(Object o) {

if (this == o) return true;

if (o == null || getClass() != o.getClass()) return false;

Person person = (Person) o;

return age == person.age && Objects.equals(name, person.name);

}

@Override

public int hashCode() {

return Objects.hash(name, age);

}

}

线程安全问题

大多数集合类都不是线程安全的,在多线程环境中使用时需要同步:

// 方式1:使用Collections的同步包装器

List synchronizedList = Collections.synchronizedList(new ArrayList<>());

Map synchronizedMap = Collections.synchronizedMap(new HashMap<>());

// 方式2:使用并发集合

List concurrentList = new CopyOnWriteArrayList<>();

Map concurrentMap = new ConcurrentHashMap<>();

比较器的一致性

当使用自定义比较器时,确保符合一致性要求:

// 不一致的比较器(违反了传递性)

Comparator inconsistentComparator = new Comparator() {

@Override

public int compare(Integer a, Integer b) {

return (a % 2) - (b % 2); // 只比较奇偶性

}

};

9. 总结与进阶学习

9.1 总结

Java集合框架提供了一组丰富的接口和实现类,用于存储和操作数据:

核心接口:Collection, List, Set, Queue, Deque, Map主要实现:ArrayList, LinkedList, HashSet, LinkedHashSet, TreeSet, HashMap, LinkedHashMap, TreeMap特殊集合:并发集合(ConcurrentHashMap等)、不可变集合(Collections.unmodifiableXXX等)工具类:Collections, Arrays

选择合适的集合类型取决于多种因素,包括访问模式、排序要求、性能需求等。

9.2 进阶学习路径

要深入学习Java集合框架,可以考虑以下进阶主题:

Java 8+ Stream API:更加函数式的集合操作方式并发集合和原子操作:java.util.concurrent包中的类第三方集合库:如Apache Commons Collections, Google Guava集合性能优化:如何优化集合操作的性能数据结构原理:学习集合类背后的数据结构

9.3 学习资源

书籍:

“Java核心技术”(Horstmann & Cornell)“Effective Java”(Joshua Bloch)“Java编程思想”(Bruce Eckel)

在线资源:

Java官方文档:https://docs.oracle.com/en/java/javase/Java Tutorials: https://docs.oracle.com/javase/tutorial/collections/Baeldung关于Java集合的文章:https://www.baeldung.com/java-collections