Java集合框架深度解析:Collection与Map体系详解
集合框架概述
Java集合框架主要分为两大体系:单列集合(Collection)和双列集合(Map)。其中Collection接口是所有单列集合的根接口,而Map则处理键值对数据。
单列集合:Collection接口
单列集合的特点是每次只能操作一个元素。当集合存储自定义对象时,务必重写equals()方法,否则默认比较的是对象引用地址,可能导致元素删除或判断逻辑异常。
Collection通用遍历方式
1. 迭代器遍历
通过Iterator对象依次访问集合元素,支持在遍历过程中安全删除元素。
2. 增强for循环
语法简洁,但底层依然依赖迭代器实现。编译后的字节码文件会还原为迭代器模式。
3. forEach方法
基于函数式编程思想,需传入Consumer接口实现。推荐使用Lambda表达式简化代码:
Collection<String> items = new ArrayList<>();
items.add("Java");
items.add("Python");
items.add("Go");
// 匿名内部类方式
items.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
// Lambda简化写法
items.forEach(s -> System.out.println(s));
List接口详解
List系列集合具有以下特征:元素有序存储、支持索引访问、允许重复元素。
List特有方法
add(int index, E element):指定位置插入remove(int index):删除指定索引元素get(int index):获取指定索引元素set(int index, E element):修改指定位置元素
并发修改异常处理
遍历List时直接调用集合的增删方法会引发ConcurrentModificationException。解决方案:
- 使用迭代器的
remove()方法 - 采用普通for循环手动控制索引
- 使用并发集合类(如CopyOnWriteArrayList)
ListIterator双向遍历
ListIterator扩展了Iterator功能,支持逆向遍历。使用前必须先正向遍历到末尾:
List<String> data = new ArrayList<>();
data.add("alpha");
data.add("beta");
data.add("gamma");
ListIterator<String> li = data.listIterator();
while (li.hasNext()) {
System.out.println(li.next());
}
System.out.println("---反向输出---");
while (li.hasPrevious()) {
System.out.println(li.previous());
}
ArrayList
底层采用动态数组实现,查询效率高(O(1)),增删效率低(需移动元素)。线程不安全,适合单线程环境。
Vector
与ArrayList类似但线程安全(方法使用synchronized修饰)。扩容机制如下:
- 无参构造:初始容量10,每次扩容为原容量2倍
- 指定初始容量:扩容倍率同无参构造
- 指定容量增量:每次增加指定值
// Vector扩容核心逻辑
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity,
capacityIncrement > 0 ? capacityIncrement : oldCapacity);
return elementData = Arrays.copyOf(elementData, newCapacity);
}
Stack
继承Vector,实现后进先出(LIFO)栈结构。提供push、pop、peek、empty、search等标准栈操作。
LinkedList
底层采用双向链表实现,增删效率高(O(1)),查询效率低(O(n))。虽实现List接口支持索引访问,但get(index)方法仍需遍历链表。
Set接口剖析
Set集合保证元素唯一性,无索引,存取顺序不确定(除特定实现外)。
TreeSet
基于红黑树实现,自动对元素排序。支持两种排序机制:
自然排序
元素类实现Comparable接口并重写compareTo方法:
public class Student implements Comparable<Student> {
private String name;
private int age;
@Override
public int compareTo(Student other) {
// 按年龄升序,年龄相同按姓名排序
if (this.age != other.age) {
return this.age - other.age;
}
return this.name.compareTo(other.name);
}
}
比较器排序
创建TreeSet时传入Comparator实现,优先级高于自然排序:
// Lambda方式
TreeSet<Student> sortedSet = new TreeSet<>((s1, s2) -> {
if (s1.getAge() != s2.getAge()) {
return s2.getAge() - s1.getAge(); // 按年龄降序
}
return s2.getName().compareTo(s1.getName()); // 姓名降序
});
HashSet
基于哈希表实现,依赖HashMap存储数据。保证元素唯一性的关键在于同时重写hashCode()和equals()方法:
- 相同对象必须产生相同的哈希码
- 哈希码相同时,equals方法需能正确判断内容是否一致
LinkedHashSet
继承HashSet,额外维护双向链表记录插入顺序。兼具HashSet的去重特性和可预测的迭代顺序。
集合使用场景建议
- 需要快速随机访问:ArrayList
- 频繁增删操作:LinkedList
- 需要排序:TreeSet/TreeMap
- 需要唯一性:HashSet/HashMap
- 需要保持插入顺序:LinkedHashSet/LinkedHashMap
- 线程安全:Vector/Collections.synchronizedXxx
Collection工具类
Collections提供sort、binarySearch、shuffle等静态方法操作集合。部分方法支持传入比较器自定义排序规则。
双列集合:Map接口
Map存储键值对,键唯一、值可重复。底层数据结构仅针对键有效。
Map核心方法
put(K key, V value):添加/修改键值对remove(Object key):根据键删除get(Object key):根据键获取值containsKey(Object key):检查键是否存在
Map遍历方式
三种经典遍历方法:
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 95);
scores.put("Bob", 88);
// 1. 键找值
for (String name : scores.keySet()) {
System.out.println(name + ": " + scores.get(name));
}
// 2. 遍历Entry对象
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(entry.getKey() + " - " + entry.getValue());
}
// 3. forEach + Lambda
scores.forEach((k, v) -> System.out.println(k + " = " + v));
Map实现类对比
HashMap
基于哈希表实现,键唯一(需重写hashCode和equals)。允许null键和null值,非线程安全。
TreeMap
红黑树实现,自动对键排序。键类需实现Comparable接口或传入比较器。
LinkedHashMap
继承HashMap,额外维护双向链表确保迭代顺序与插入顺序一致。适合需要可预测遍历顺序的场景。