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<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()
- 获取并移除队首元素,如果队列为空则返回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<K> keySet()
- 返回包含所有键的SetCollection<V> values()
- 返回包含所有值的CollectionSet<Map.Entry<K, V>> 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<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
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<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
- 高并发、高性能的线程安全HashMapCopyOnWriteArrayList
- 线程安全的ArrayList,适用于读多写少的场景CopyOnWriteArraySet
- 使用CopyOnWriteArrayList实现的线程安全SetConcurrentLinkedQueue
- 线程安全的非阻塞队列ConcurrentLinkedDeque
- 线程安全的非阻塞双端队列ConcurrentSkipListMap
- 线程安全的NavigableMapConcurrentSkipListSet
- 线程安全的NavigableSetArrayBlockingQueue
- 基于数组的有界阻塞队列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提供了几种方式来创建不可变集合:
-
Collections工具类
Collections.unmodifiableXXX(XXX<T> c)
- 返回不可修改的视图
-
List.of()、Set.of()和Map.of()方法(Java 9+)
- 创建不可变的集合
-
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提供了多种方式来遍历集合:
- 使用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();
}
}
- 使用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<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++]
- 使用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<String> treeSet = new TreeSet<>();
treeSet.add("C++");
treeSet.add("Python");
treeSet.add("Java");
System.out.println(treeSet); // 输出: [C++, Java, Python]
- 使用Stream API排序(Java 8+)
List<String> sortedList = list.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(sortedList); // 输出: [C++, Java, Python]
6.3 集合间的转换
- 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);
- 数组转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());
- 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());
- 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集合框架提供了丰富的实现,但有时您可能需要创建自定义的集合类。
创建自定义集合的方式:
- 扩展现有集合类
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;
}
}
- 实现集合接口
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);
}
}
}
- 使用组合而非继承
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队列:
LinkedList
或ArrayDeque
- 需要LIFO栈:
ArrayDeque
- 需要优先级队列:
PriorityQueue
- 需要线程安全:考虑
java.util.concurrent
包中的集合
8.2 性能考虑
各种集合操作的时间复杂度:
集合类型 | 添加 | 删除 | 获取 | 包含 | 迭代 |
---|---|---|---|---|---|
ArrayList | O(1)* | O(n) | O(1) | O(n) | O(n) |
LinkedList | O(1) | O(1)* | O(n) | O(n) | O(n) |
HashSet | O(1) | O(1) | N/A | O(1) | O(n) |
LinkedHashSet | O(1) | O(1) | N/A | O(1) | O(n) |
TreeSet | O(log n) | O(log n) | N/A | O(log n) | O(n) |
HashMap | O(1) | O(1) | O(1) | O(1) | O(n) |
LinkedHashMap | O(1) | O(1) | O(1) | O(1) | O(n) |
TreeMap | O(log n) | O(log n) | O(log n) | O(log n) | O(n) |
*:平均情况,最坏情况可能是O(n)
性能优化技巧:
- 为集合预分配容量,避免频繁扩容
- 使用适当的初始容量和负载因子
- 优先使用批量操作(如
addAll
) - 避免在for-each循环中删除元素
- 在合适的场景使用并行流
8.3 常见陷阱
- 并发修改异常
在迭代过程中修改集合可能会导致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"));
- 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"); // 正常工作
- 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}
- 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<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<>();
- 比较器的一致性
当使用自定义比较器时,确保符合一致性要求:
// 不一致的比较器(违反了传递性)
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集合框架,可以考虑以下进阶主题:
- 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