3293 字
16 分钟

Java集合框架详解

2026-02-06
浏览量 加载中...

Java集合框架详解#

什么是集合框架?#

集合框架是Java提供的一组用于存储和操作对象的接口和类,它提供了统一的方式来管理和操作数据集合。

集合框架的层次结构#

Java集合框架主要包含以下接口:

1. Collection接口#

Collection是所有集合的根接口,它定义了集合的基本操作。

  • List:有序集合,允许重复元素
  • Set:无序集合,不允许重复元素
  • Queue:队列,先进先出

2. Map接口#

Map接口用于存储键值对映射,它不直接继承自Collection接口。

  • HashMap:基于哈希表实现,无序
  • LinkedHashMap:基于哈希表和链表实现,有序(插入顺序)
  • TreeMap:基于红黑树实现,有序(自然顺序或比较器顺序)
  • Hashtable:线程安全的HashMap,效率较低

List接口#

List接口是有序集合,允许重复元素,它提供了根据索引访问元素的方法。

List的实现类#

1. ArrayList#

  • 底层实现:基于动态数组
  • 特点:查询快,增删慢
  • 适用场景:查询操作频繁的场景

2. LinkedList#

  • 底层实现:基于双向链表
  • 特点:查询慢,增删快
  • 适用场景:增删操作频繁的场景

3. Vector#

  • 底层实现:基于动态数组
  • 特点:线程安全,效率较低
  • 适用场景:多线程环境

List的常用方法#

List<String> list = new ArrayList<>();
// 添加元素
list.add("元素1"); // 添加到末尾
list.add(1, "元素2"); // 添加到指定位置
// 获取元素
String element = list.get(0);
// 修改元素
list.set(0, "新元素1");
// 删除元素
list.remove(0); // 根据索引删除
list.remove("元素2"); // 根据元素删除
// 查找元素
int index = list.indexOf("元素1"); // 查找元素的索引
boolean contains = list.contains("元素1"); // 检查元素是否存在
// 其他方法
int size = list.size(); // 获取集合大小
boolean isEmpty = list.isEmpty(); // 检查集合是否为空
list.clear(); // 清空集合
Object[] array = list.toArray(); // 转换为数组

List的遍历方式#

1. 普通for循环#

for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}

2. 增强型for循环#

for (String element : list) {
System.out.println(element);
}

3. 迭代器#

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
}

4. ListIterator#

ListIterator<String> listIterator = list.listIterator();
while (listIterator.hasNext()) {
String element = listIterator.next();
System.out.println(element);
}
// 反向遍历
while (listIterator.hasPrevious()) {
String element = listIterator.previous();
System.out.println(element);
}

Set接口#

Set接口是无序集合,不允许重复元素,它继承自Collection接口。

Set的实现类#

1. HashSet#

  • 底层实现:基于哈希表
  • 特点:无序,不允许重复元素,查询速度快
  • 适用场景:需要快速查找且不关心顺序的场景

2. LinkedHashSet#

  • 底层实现:基于哈希表和链表
  • 特点:有序(插入顺序),不允许重复元素
  • 适用场景:需要保持插入顺序的场景

3. TreeSet#

  • 底层实现:基于红黑树
  • 特点:有序(自然顺序或比较器顺序),不允许重复元素
  • 适用场景:需要排序的场景

Set的常用方法#

Set<String> set = new HashSet<>();
// 添加元素
set.add("元素1");
set.add("元素2");
set.add("元素1"); // 重复元素,不会添加
// 删除元素
set.remove("元素1");
// 查找元素
boolean contains = set.contains("元素1");
// 其他方法
int size = set.size();
boolean isEmpty = set.isEmpty();
set.clear();
Object[] array = set.toArray();

Set的遍历方式#

1. 增强型for循环#

for (String element : set) {
System.out.println(element);
}

2. 迭代器#

Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
}

Map接口#

Map接口用于存储键值对映射,它提供了根据键访问值的方法。

Map的实现类#

1. HashMap#

  • 底层实现:基于哈希表
  • 特点:无序,键不允许重复
  • 适用场景:需要快速查找的场景

2. LinkedHashMap#

  • 底层实现:基于哈希表和链表
  • 特点:有序(插入顺序),键不允许重复
  • 适用场景:需要保持插入顺序的场景

3. TreeMap#

  • 底层实现:基于红黑树
  • 特点:有序(自然顺序或比较器顺序),键不允许重复
  • 适用场景:需要排序的场景

4. Hashtable#

  • 底层实现:基于哈希表
  • 特点:线程安全,效率较低,键不允许重复
  • 适用场景:多线程环境

Map的常用方法#

Map<String, Integer> map = new HashMap<>();
// 添加元素
map.put("键1", 1);
map.put("键2", 2);
// 获取元素
Integer value = map.get("键1");
// 修改元素
map.put("键1", 10); // 覆盖原有值
// 删除元素
map.remove("键1");
// 查找元素
boolean containsKey = map.containsKey("键1"); // 检查键是否存在
boolean containsValue = map.containsValue(1); // 检查值是否存在
// 其他方法
int size = map.size();
boolean isEmpty = map.isEmpty();
map.clear();
// 获取所有键
Set<String> keys = map.keySet();
// 获取所有值
Collection<Integer> values = map.values();
// 获取所有键值对
Set<Map.Entry<String, Integer>> entries = map.entrySet();

Map的遍历方式#

1. 遍历键#

for (String key : map.keySet()) {
Integer value = map.get(key);
System.out.println(key + ": " + value);
}

2. 遍历键值对#

for (Map.Entry<String, Integer> entry : map.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + ": " + value);
}

3. 遍历值#

for (Integer value : map.values()) {
System.out.println(value);
}

队列和栈#

Queue接口#

Queue接口是队列,它提供了先进先出(FIFO)的操作方式。

Queue的实现类#

  • LinkedList:实现了Queue接口
  • PriorityQueue:优先级队列,基于堆实现

Queue的常用方法#

Queue<String> queue = new LinkedList<>();
// 添加元素
queue.offer("元素1"); // 添加失败返回false
queue.add("元素2"); // 添加失败抛出异常
// 获取并移除队首元素
String element = queue.poll(); // 队列为空返回null
String element = queue.remove(); // 队列为空抛出异常
// 获取队首元素但不移除
String element = queue.peek(); // 队列为空返回null
String element = queue.element(); // 队列为空抛出异常

Stack类#

Stack类是栈,它提供了后进先出(LIFO)的操作方式。

Stack的常用方法#

Stack<String> stack = new Stack<>();
// 压栈
stack.push("元素1");
stack.push("元素2");
// 出栈
String element = stack.pop(); // 栈为空抛出异常
// 查看栈顶元素
String element = stack.peek(); // 栈为空抛出异常
// 检查栈是否为空
boolean isEmpty = stack.isEmpty();
// 获取栈大小
int size = stack.size();

并发集合#

Java提供了线程安全的并发集合,它们位于java.util.concurrent包中。

并发集合的实现类#

1. ConcurrentHashMap#

  • 特点:线程安全的HashMap,支持高并发读写
  • 适用场景:多线程环境下的哈希表操作

2. CopyOnWriteArrayList#

  • 特点:线程安全的ArrayList,适用于读多写少的场景
  • 适用场景:多线程环境下的读操作频繁的场景

3. CopyOnWriteArraySet#

  • 特点:基于CopyOnWriteArrayList实现,线程安全的Set
  • 适用场景:多线程环境下的读操作频繁的场景

4. ConcurrentLinkedQueue#

  • 特点:线程安全的无界队列
  • 适用场景:多线程环境下的队列操作

5. BlockingQueue#

  • 特点:阻塞队列,支持线程安全的入队和出队操作
  • 实现类:ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue等
  • 适用场景:多线程环境下的生产者-消费者模式

集合工具类#

Collections类#

Collections类提供了操作集合的静态方法。

Collections的常用方法#

// 排序
Collections.sort(list); // 自然排序
Collections.sort(list, comparator); // 自定义排序
// 查找
int index = Collections.binarySearch(list, key); // 二分查找
Object max = Collections.max(collection); // 最大值
Object min = Collections.min(collection); // 最小值
// 填充
Collections.fill(list, value); // 填充所有元素
// 拷贝
Collections.copy(dest, src); // 拷贝集合
// 反转
Collections.reverse(list); // 反转集合
// 随机打乱
Collections.shuffle(list); // 随机打乱集合
// 替换
Collections.replaceAll(list, oldValue, newValue); // 替换所有元素
// 线程安全
List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());
Set<String> synchronizedSet = Collections.synchronizedSet(new HashSet<>());
Map<String, Integer> synchronizedMap = Collections.synchronizedMap(new HashMap<>());

Arrays类#

Arrays类提供了操作数组的静态方法,它也可以用于数组和集合的转换。

Arrays的常用方法#

// 数组转集合
List<String> list = Arrays.asList(array);
// 集合转数组
String[] array = list.toArray(new String[0]);
// 排序
Arrays.sort(array); // 自然排序
Arrays.sort(array, comparator); // 自定义排序
// 查找
int index = Arrays.binarySearch(array, key); // 二分查找
// 填充
Arrays.fill(array, value); // 填充所有元素
// 比较
boolean equals = Arrays.equals(array1, array2); // 比较两个数组
// 哈希码
int hashCode = Arrays.hashCode(array); // 获取数组的哈希码
// 字符串表示
String str = Arrays.toString(array); // 获取数组的字符串表示

集合的最佳实践#

1. 选择合适的集合#

  • 需要有序、可重复的元素:使用List

    • 查询频繁:ArrayList
    • 增删频繁:LinkedList
    • 多线程环境:Vector
  • 需要无序、不可重复的元素:使用Set

    • 不需要排序:HashSet
    • 需要保持插入顺序:LinkedHashSet
    • 需要排序:TreeSet
  • 需要键值对映射:使用Map

    • 不需要排序:HashMap
    • 需要保持插入顺序:LinkedHashMap
    • 需要排序:TreeMap
    • 多线程环境:Hashtable或ConcurrentHashMap

2. 初始化时指定容量#

对于已知大小的集合,初始化时指定容量可以提高性能。

// 好的做法
List<String> list = new ArrayList<>(100); // 指定初始容量
Map<String, Integer> map = new HashMap<>(100); // 指定初始容量

3. 使用泛型#

使用泛型可以避免类型转换错误,提高代码安全性。

// 好的做法
List<String> list = new ArrayList<>();
String element = list.get(0); // 不需要类型转换
// 坏的做法
List list = new ArrayList();
String element = (String) list.get(0); // 需要类型转换,可能出错

4. 优先使用增强型for循环#

遍历集合时,优先使用增强型for循环,代码更简洁。

// 好的做法
for (String element : list) {
System.out.println(element);
}
// 坏的做法
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}

5. 避免在循环中修改集合#

在遍历集合时修改集合,可能会导致ConcurrentModificationException异常。

// 坏的做法
for (String element : list) {
if (element.equals("删除元素")) {
list.remove(element); // 可能抛出ConcurrentModificationException
}
}
// 好的做法
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
if (element.equals("删除元素")) {
iterator.remove(); // 正确的删除方式
}
}

6. 使用集合的isEmpty()方法#

检查集合是否为空时,使用isEmpty()方法而不是size() == 0,代码更简洁,可读性更好。

// 好的做法
if (list.isEmpty()) {
System.out.println("集合为空");
}
// 坏的做法
if (list.size() == 0) {
System.out.println("集合为空");
}

7. 合理使用并发集合#

在多线程环境下,使用线程安全的并发集合,而不是手动同步。

// 好的做法
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 坏的做法
Map<String, Integer> map = Collections.synchronizedMap(new HashMap<>());

8. 注意集合的线程安全性#

大多数集合类不是线程安全的,在多线程环境下需要手动同步或使用线程安全的集合。

常见陷阱#

1. Arrays.asList()的局限性#

Arrays.asList()返回的是一个固定大小的列表,不支持添加和删除操作。

String[] array = {"a", "b", "c"};
List<String> list = Arrays.asList(array);
list.add("d"); // 抛出UnsupportedOperationException异常

2. 集合与原始类型#

集合只能存储对象,不能存储原始类型,需要使用包装类。

// 错误
List<int> list = new ArrayList<>();
// 正确
List<Integer> list = new ArrayList<>();

3. ConcurrentModificationException#

在遍历集合时修改集合,可能会抛出ConcurrentModificationException异常。

4. 内存泄漏#

集合中存储的对象如果没有被正确清理,可能会导致内存泄漏。

5. 哈希码和equals方法#

使用HashSet或HashMap时,元素或键应该正确实现hashCode()和equals()方法。

public class Person {
private String name;
private int age;
// 必须实现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 age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}

6. 集合的大小#

集合的size()方法返回的是int类型,对于非常大的集合,可能会导致溢出。

7. 自动装箱和拆箱的性能影响#

在使用集合存储包装类时,自动装箱和拆箱可能会影响性能。

总结#

Java集合框架是Java编程中的重要部分,它提供了丰富的接口和类,用于存储和操作数据集合。不同的集合类有不同的特点和适用场景,我们应该根据具体需求选择合适的集合。

本文介绍了Java集合框架的层次结构、常用集合类的特点和使用方法,以及集合的最佳实践和常见陷阱。希望本文能够帮助你更好地理解和使用Java的集合框架。

练习#

  1. 编写一个程序,使用ArrayList存储学生信息,并实现添加、删除、修改和查询操作。

  2. 编写一个程序,使用LinkedList实现一个队列。

  3. 编写一个程序,使用HashSet存储不重复的整数,并实现添加、删除和遍历操作。

  4. 编写一个程序,使用TreeSet存储字符串,并按照字典序排序。

  5. 编写一个程序,使用HashMap存储学生的姓名和成绩,并实现添加、删除、修改和查询操作。

  6. 编写一个程序,使用LinkedHashMap存储元素,并验证它保持插入顺序。

  7. 编写一个程序,使用TreeMap存储键值对,并按照键的自然顺序排序。

  8. 编写一个程序,使用Collections工具类对List进行排序、查找和反转操作。

  9. 编写一个程序,使用并发集合(如ConcurrentHashMap)在多线程环境下操作数据。

  10. 编写一个程序,使用BlockingQueue实现生产者-消费者模式。

通过这些练习,你将更加熟悉Java的集合框架,为后续的学习做好准备。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
Java集合框架详解
https://blog.vanilla.net.cn/posts/2026-02-05-java-collection-framework/
作者
鹁鸪
发布于
2026-02-06
许可协议
CC BY-NC-SA 4.0

评论区

目录