在 Java 中,集合 是一种存储元素的容器,允许快速查找特定元素。Java 提供了丰富的集合实现类和接口,每个接口都有其特定的功能。
选择合适的集合需要考虑以下因素:
- 数据类型和大小: 根据存储的数据类型和数量选择合适的数据结构。
- 性能要求: 操作频率高时,优化性能的集合类很重要。
- 同步需求:如果需要线程安全,可以选择带并发操作的支持集合。
一、集合框架全景图
1.1 集合框架体系结构
1.2 核心接口对比
接口 | 特点 | 典型实现类 | 时间复杂度 |
---|---|---|---|
List | 有序,可重复 | ArrayList/LinkedList | 随机访问O(1)/O(n) |
Set | 无序,唯一 | HashSet/TreeSet | 添加/查找O(1)/O(logn) |
Map | 键值对,键唯一 | HashMap/TreeMap | 添加/查找O(1)/O(logn) |
Queue | 先进先出(FIFO) | LinkedList/PriorityQueue | 入队出队O(1)/O(logn) |
二、List接口详解
2.1 ArrayList(动态数组)
// 创建与基本操作
List<String> list = new ArrayList<>();
list.add("Apple"); // 添加元素
list.add(0, "Banana"); // 指定位置插入
String fruit = list.get(1); // 获取元素(索引从0开始)
list.remove("Apple"); // 删除元素
// 遍历方式
for (int i = 0; i < list.size(); i++) { // 随机访问
System.out.println(list.get(i));
}
for (String s : list) { // foreach遍历
System.out.println(s);
}
// 转换为数组
String[] array = list.toArray(new String[0]);
特点:
- 底层基于Object[]数组
- 默认初始容量10,扩容1.5倍
- 适合随机访问,插入删除效率低
2.2 LinkedList(双向链表)
List<Integer> linkedList = new LinkedList<>();
linkedList.add(10);
linkedList.addFirst(5); // 头部插入
linkedList.addLast(20); // 尾部插入
// 使用迭代器高效删除
Iterator<Integer> it = linkedList.iterator();
while (it.hasNext()) {
if (it.next() == 10) {
it.remove(); // 安全删除
}
}
特点:
- 每个元素包含前后节点指针
- 插入删除O(1),查找O(n)
- 实现Deque接口,可用作队列/栈
三、Set接口深入解析
3.1 HashSet(哈希集合)
Set<String> names = new HashSet<>();
names.add("Alice");
names.add("Bob");
names.add("Alice"); // 重复元素被忽略
// 快速判断存在性
if (names.contains("Bob")) {
System.out.println("Bob存在");
}
// 遍历无序
for (String name : names) {
System.out.println(name); // 输出顺序不确定
}
实现原理:
- 基于HashMap实现(值存储于HashMap的Key)
- 依赖hashCode()和equals()方法
- 最佳实践:重写对象的hashCode和equals
3.2 TreeSet(有序集合)
Set<Integer> scores = new TreeSet<>();
scores.add(90);
scores.add(85);
scores.add(95);
// 自动排序遍历
for (int score : scores) {
System.out.println(score); // 输出85,90,95
}
// 范围查询
Set<Integer> subSet = ((TreeSet<Integer>)scores).subSet(85, true, 90, true);
特点:
- 基于红黑树实现
- 元素必须实现Comparable接口
- 查找时间复杂度O(logn)
四、Map接口核心实现
4.1 HashMap(哈希映射)
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 10);
map.put("Banana", 5);
// 获取值(注意空指针)
Integer count = map.get("Apple");
// 遍历EntrySet
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// Java8遍历方式
map.forEach((k, v) -> System.out.println(k + " => " + v));
重要特性:
- 初始容量16,负载因子0.75
- 哈希冲突通过链表/红黑树解决(JDK8+)
- Key的不可变性要求
4.2 LinkedHashMap(有序哈希映射)
Map<String, Integer> orderedMap = new LinkedHashMap<>();
orderedMap.put("A", 1);
orderedMap.put("C", 3);
orderedMap.put("B", 2);
// 保持插入顺序遍历
orderedMap.forEach((k, v) -> System.out.println(k)); // 输出A,C,B
应用场景:需要保持插入顺序的缓存系统
五、集合工具类Collections
5.1 常用工具方法
List<Integer> numbers = Arrays.asList(3,1,4,1,5,9);
// 排序
Collections.sort(numbers); // [1,1,3,4,5,9]
// 洗牌
Collections.shuffle(numbers);
// 不可变集合
List<Integer> immutable = Collections.unmodifiableList(numbers);
// 同步包装
List<Integer> syncList = Collections.synchronizedList(numbers);
5.2 集合初始化技巧
// 快速初始化List
List<String> cities = new ArrayList<>() {{
add("北京");
add("上海");
add("广州");
}};
// 使用Stream初始化
List<Integer> nums = Stream.of(1,2,3).collect(Collectors.toList());
六、最佳实践指南
6.1 集合选择策略
需求场景 | 推荐实现类 | 理由 |
---|---|---|
频繁随机访问 | ArrayList | O(1)访问效率 |
频繁插入删除 | LinkedList | O(1)插入删除 |
去重存储 | HashSet | O(1)查找 |
需要排序 | TreeSet | 自动排序 |
键值对快速查找 | HashMap | 最优哈希表实现 |
需要插入顺序 | LinkedHashMap | 维护插入顺序 |
6.2 性能优化要点
- 设置初始容量:避免频繁扩容
new ArrayList<>(100); // 指定初始容量
- 避免装箱开销:使用原始类型集合
IntArrayList (Eclipse Collections)
- 选择合适迭代器:
// ArrayList使用普通for更快 for (int i=0; i<list.size(); i++) {} // LinkedList使用迭代器更优 Iterator<Integer> it = list.iterator();
6.3 线程安全方案
方案 | 优点 | 缺点 |
---|---|---|
Vector | 简单易用 | 全表锁,性能差 |
Collections.synchronizedXXX | 灵活包装 | 同Vector |
ConcurrentHashMap | 分段锁,高并发 | 不保证强一致性 |
CopyOnWriteArrayList | 读无锁,写时复制 | 内存消耗大,适合读多写少 |
七、常见问题解答
Q1:如何避免ConcurrentModificationException?
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
// 错误方式(抛出异常)
for (String s : list) {
if (s.equals("B")) {
list.remove(s);
}
}
// 正确方式:使用迭代器删除
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().equals("B")) {
it.remove();
}
}
Q2:HashMap与HashTable的区别?
特性 | HashMap | HashTable |
---|---|---|
线程安全 | 否 | 是 |
null支持 | 允许key/value为null | 不允许 |
迭代器 | fail-fast | 不保证 |
性能 | 更高 | 较低 |
通过本文的学习,您已经掌握了Java集合框架的核心知识。关键要点总结:
- List:有序集合首选ArrayList,频繁插入删除用LinkedList
- Set:快速去重用HashSet,需要排序用TreeSet
- Map:常规需求HashMap,保持顺序LinkedHashMap
- 线程安全:高并发场景使用ConcurrentHashMap
- 工具类:善用Collections简化操作
建议通过以下步骤巩固学习:
- 编写各集合的CRUD示例
- 对比不同集合的性能差异
- 实现一个基于集合的缓存系统
- 阅读ArrayList/HashMap的JDK源码
掌握集合框架是Java开发的基石,合理选择数据结构能让您的程序更高效、更健壮!