Java集合框架全面详解

1. 集合框架概述

1.1 什么是集合框架?

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

1.2 为什么需要集合框架?

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

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

1.3 集合框架的组成部分

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

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

2. 核心接口

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

2.1 Collection接口

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

主要方法:

  • boolean add(E e) - 添加元素
  • boolean remove(Object o) - 移除元素
  • boolean contains(Object o) - 检查是否包含指定元素
  • int size() - 返回集合中的元素数量
  • boolean isEmpty() - 检查集合是否为空
  • Iterator<E> iterator() - 返回迭代器
  • void clear() - 清空集合
  • boolean containsAll(Collection<?> c) - 检查是否包含另一个集合的所有元素
  • boolean addAll(Collection<? extends E> 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<E> 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() - 获取并移除队首元素,如果队列为空则返回null
  • E peek() - 获取但不移除队首元素,如果队列为空则返回null
  • E 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() - 获取并移除队列前端/后端的元素,如果队列为空则返回null
  • E 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<K> keySet() - 返回包含所有键的Set
  • Collection<V> values() - 返回包含所有值的Collection
  • Set<Map.Entry<K, V>> entrySet() - 返回包含所有键值对的Set
  • int 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> treeSet = new TreeSet<>();
treeSet.add("Java");
treeSet.add("Python");
treeSet.add("C++");
System.out.println(treeSet); // 输出: [C++, Java, Python](按字母顺序)

// 使用比较器
TreeSet<String> 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<Integer> 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<String> queue = new ArrayDeque<>();
queue.offer("Java");
queue.offer("Python");
queue.offer("C++");
System.out.println(queue.poll()); // 输出: Java

// 作为栈使用
Deque<String> 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<String, Integer> 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<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("Java", 1995);
linkedHashMap.put("Python", 1991);
linkedHashMap.put("C++", 1983);
System.out.println(linkedHashMap); // 输出保持插入顺序

// 按访问顺序(LRU缓存)
Map<String, Integer> 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<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Java", 1995);
treeMap.put("Python", 1991);
treeMap.put("C++", 1983);
System.out.println(treeMap); // 输出按键的字母顺序排序

// 使用比较器
TreeMap<String, Integer> 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<String, Integer> 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

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

特点:

  • 继承自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<T> list) - 使用自然顺序对列表进行排序
    • sort(List<T> list, Comparator<? super T> c) - 使用比较器对列表进行排序
  • 查找
    • binarySearch(List<? extends Comparable<? super T>> list, T key) - 使用二分查找算法查找元素
    • max(Collection<? extends T> coll) - 返回最大元素
    • min(Collection<? extends T> coll) - 返回最小元素
    • frequency(Collection<?> c, Object o) - 返回指定元素在集合中出现的次数
  • 修改
    • reverse(List<?> list) - 反转列表
    • shuffle(List<?> list) - 随机打乱列表
    • swap(List<?> list, int i, int j) - 交换列表中的两个元素
    • fill(List<? super T> list, T obj) - 用指定元素替换列表中的所有元素
    • copy(List<? super T> dest, List<? extends T> src) - 复制列表
    • rotate(List<?> list, int distance) - 旋转列表
    • replaceAll(List<T> list, T oldVal, T newVal) - 替换列表中所有的指定元素
  • 特殊集合
    • unmodifiableXXX(XXX<? extends T> c) - 返回不可修改的视图
    • synchronizedXXX(XXX<T> c) - 返回同步的视图
    • checkedXXX(XXX<E> c, Class<E> type) - 返回类型检查的视图
    • emptyXXX() - 返回空集合
    • singletonXXX(T o) - 返回只包含一个元素的集合

示例:

List<Integer> 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<Integer> unmodifiableList = Collections.unmodifiableList(numbers);
try {
    unmodifiableList.add(4); // 抛出UnsupportedOperationException
} catch (UnsupportedOperationException e) {
    System.out.println("Cannot modify unmodifiable list");
}

// 同步集合
List<Integer> synchronizedList = Collections.synchronizedList(numbers);

// 单例集合
Set<Integer> singletonSet = Collections.singleton(1);

4.2 Arrays类

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

主要方法:

  • 排序
    • sort(T[] a) - 使用自然顺序对数组进行排序
    • sort(T[] a, Comparator<? super T> 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<? extends T> 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<Integer> 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<E> - 元素枚举接口,类似于Iterator

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

5.2 并发集合

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

主要类:

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

示例:

// ConcurrentHashMap
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("Java", 1995);
concurrentMap.put("Python", 1991);
System.out.println(concurrentMap.get("Java")); // 输出: 1995

// CopyOnWriteArrayList
List<String> copyOnWriteList = new CopyOnWriteArrayList<>();
copyOnWriteList.add("Java");
copyOnWriteList.add("Python");
// 适合多线程读操作多,写操作少的场景

// ArrayBlockingQueue
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(10); // 容量为10
blockingQueue.put("Java"); // 如果队列已满,put方法会阻塞
String item = blockingQueue.take(); // 如果队列为空,take方法会阻塞
System.out.println(item); // 输出: Java

5.3 不可变集合

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

  1. Collections工具类

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

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

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

示例:

// 使用Collections
List<String> modifiableList = new ArrayList<>();
modifiableList.add("Java");
modifiableList.add("Python");
List<String> unmodifiableList = Collections.unmodifiableList(modifiableList);

// 使用Java 9+的工厂方法
List<String> immutableList = List.of("Java", "Python", "C++");
Set<String> immutableSet = Set.of("Java", "Python", "C++");
Map<String, Integer> 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提供了多种方式来遍历集合:

  1. 使用Iterator
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String element = iterator.next();
    System.out.println(element);
    
    // 安全地删除元素
    if (element.equals("Python")) {
        iterator.remove();
    }
}
  1. 使用for-each循环
for (String element : list) {
    System.out.println(element);
    // 注意: 在for-each循环中不能安全地修改集合
}
  1. 使用索引(仅适用于List)
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}
  1. 使用forEach方法(Java 8+)
list.forEach(element -> System.out.println(element));
// 或使用方法引用
list.forEach(System.out::println);
  1. 使用Stream API(Java 8+)
list.stream()
    .filter(element -> element.startsWith("J"))
    .forEach(System.out::println);

6.2 排序集合

  1. 使用Collections.sort()
List<String> 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++]
  1. 使用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++]
  1. 使用TreeSet或TreeMap自动排序
Set<String> treeSet = new TreeSet<>();
treeSet.add("C++");
treeSet.add("Python");
treeSet.add("Java");
System.out.println(treeSet); // 输出: [C++, Java, Python]
  1. 使用Stream API排序(Java 8+)
List<String> sortedList = list.stream()
    .sorted()
    .collect(Collectors.toList());
System.out.println(sortedList); // 输出: [C++, Java, Python]

6.3 集合间的转换

  1. Collection转数组
List<String> 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);
  1. 数组转Collection
String[] array = {"Java", "Python", "C++"};

// 使用Arrays.asList() - 返回固定大小的List
List<String> list = Arrays.asList(array);

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

// 使用List.of() - 返回不可变List(Java 9+)
List<String> immutableList = List.of(array);

// 使用Stream API(Java 8+)
List<String> streamList = Arrays.stream(array).collect(Collectors.toList());
Set<String> streamSet = Arrays.stream(array).collect(Collectors.toSet());
  1. Collection之间的转换
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("Java"); // 重复元素

// List转Set(移除重复元素)
Set<String> set = new HashSet<>(list);
System.out.println(set); // 输出: [Java, Python]

// Set转List
List<String> newList = new ArrayList<>(set);

// 使用Stream API
List<String> streamList = set.stream().collect(Collectors.toList());
Set<String> streamSet = list.stream().collect(Collectors.toSet());
  1. Map与Collection的转换
Map<String, Integer> map = new HashMap<>();
map.put("Java", 1995);
map.put("Python", 1991);

// 获取键的集合
Set<String> keys = map.keySet();

// 获取值的集合
Collection<Integer> values = map.values();

// 获取键值对的集合
Set<Map.Entry<String, Integer>> entries = map.entrySet();

// Collection转Map(使用Stream API)
List<String> list = Arrays.asList("Java", "Python", "C++");
Map<String, Integer> 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<String> stringList = new ArrayList<>();
stringList.add("Java");
// stringList.add(1); // 编译错误,只能添加String类型
String s2 = stringList.get(0); // 不需要强制类型转换

泛型通配符:

  • <?> - 无界通配符,表示任何类型
  • <? extends T> - 上界通配符,表示T或T的子类型
  • <? super T> - 下界通配符,表示T或T的超类型

示例:

// 使用上界通配符
List<? extends Number> numbers = new ArrayList<Integer>(); // 合法
// numbers.add(1); // 不合法,不能添加元素
Number n = numbers.get(0); // 合法,可以获取元素

// 使用下界通配符
List<? super Integer> integers = new ArrayList<Number>(); // 合法
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<Student> {
    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<Student> 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<Student> 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<Student>() {
    @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<Student> byAgeDesc = Comparator.comparingInt(Student::getAge).reversed();
Comparator<Student> byNameThenAgeDesc = Comparator.comparing(Student::getName)
                                                  .thenComparing(byAgeDesc);
students.sort(byNameThenAgeDesc);

7.3 自定义集合实现

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

创建自定义集合的方式:

  1. 扩展现有集合类
public class LoggingArrayList<E> extends ArrayList<E> {
    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;
    }
}
  1. 实现集合接口
public class SimpleArrayList<E> implements List<E> {
    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);
        }
    }
}
  1. 使用组合而非继承
public class CountingSet<E> implements Set<E> {
    private final Set<E> delegate;
    private int addCount = 0;
    
    public CountingSet(Set<E> delegate) {
        this.delegate = delegate;
    }
    
    @Override
    public boolean add(E e) {
        addCount++;
        return delegate.add(e);
    }
    
    @Override
    public boolean addAll(Collection<? extends E> 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队列:LinkedListArrayDeque
  • 需要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 常见陷阱

  1. 并发修改异常

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

List<String> 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<String> 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"));
  1. Arrays.asList()的固定大小

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

String[] array = {"Java", "Python", "C++"};
List<String> list = Arrays.asList(array);

try {
    list.add("Go"); // 抛出UnsupportedOperationException
} catch (UnsupportedOperationException e) {
    System.out.println("Cannot add to fixed-size list");
}

// 解决方案:转换为ArrayList
List<String> modifiableList = new ArrayList<>(Arrays.asList(array));
modifiableList.add("Go"); // 正常工作
  1. Map.keySet()和values()返回的视图

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

Map<String, Integer> map = new HashMap<>();
map.put("Java", 1995);
map.put("Python", 1991);

Set<String> keys = map.keySet();
keys.remove("Java"); // 同时从map中移除Java键值对

System.out.println(map); // 输出: {Python=1991}
  1. 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. 线程安全问题

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

// 方式1:使用Collections的同步包装器
List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());
Map<String, Integer> synchronizedMap = Collections.synchronizedMap(new HashMap<>());

// 方式2:使用并发集合
List<String> concurrentList = new CopyOnWriteArrayList<>();
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
  1. 比较器的一致性

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

// 不一致的比较器(违反了传递性)
Comparator<Integer> inconsistentComparator = new Comparator<Integer>() {
    @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集合框架,可以考虑以下进阶主题:

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

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值