Java集合详解
1. 集合概述
Java为了方便对多个对象进行操作,我们可以将多个对象存储到集合中进行对象的操作。
集合类属于工具类,位于java.util包中。
集合的特点
-
只存储对象:集合类只用于存储对象,不能存储基本数据类型。
-
长度可变:集合长度是可变的,可以动态增加或删除元素。
-
类型灵活:一个集合可以存储多个不同类型的对象,但为了避免运行时出现类型不一致异常,JDK1.5后我们可以使用泛型来限定类型。
2. 集合类关系图
3. 主要集合接口和类
接口层次结构
Collection:单元素集合的顶层接口
├── List:有序集合顶层接口
│ ├── ArrayList:数组数据结构
│ ├── LinkedList:链表数据结构
│ └── Vector:已过时,数组数据结构,线程安全
└── Set:无序集合顶层接口(无重复元素)
├── HashSet:根据元素的哈希表来存储元素
└── TreeSet:树形结构的集合
Map:双元素键值对集合,顶层接口
├── Hashtable:JDK1.2之前就存在,底层是哈希表
├── HashMap:哈希表数据结构
└── TreeMap:二叉树无序集合
Iterator:遍历集合的迭代器(顶层接口)
4. Iterator迭代器接口
Iterator可以迭代获取List、Set集合中的元素。
主要方法
| 方法名 | 功能描述 |
|---|---|
hasNext() | 判断是否有下一个元素 |
next() | 返回下一个元素 |
remove() | 删除当前元素 |
扩展:ListIterator
Iterator有个子接口ListIterator,此接口对象在遍历时可以对List集合进行增删改查的操作。
5. Collection接口
Collection是所有集合类的父接口,只要是集合,它必定实现了Collection的方法。
常用方法
| 方法名 | 功能描述 |
|---|---|
add() | 添加元素 |
addAll() | 添加一组元素 |
clear() | 清空容器 |
remove() | 移除一个元素 |
removeAll() | 移除一堆元素 |
contains() | 判断是否包含某元素 |
equals() | 比较某元素 |
hashCode() | 返回哈希值 |
isEmpty() | 判断是否有元素 |
size() | 获取集合长度 |
retainAll() | 取交集,集合中只保留与第二个集合交集的元素 |
toArray() | 转换成数组 |
iterator() | 生产一个迭代器,返回Iterator接口类型的对象 |
Collection常见的有两个子接口:List和Set,它们的数据存储结构不同。最主要的是List集合是有序的,有索引位置,可以用角标来获取操作元素。
6. List接口详解
List特点
- 有序集合:具有索引位置的有序元素集合
- 支持角标操作:具有一系列能使用角标位置操作元素的方法
List特有方法
| 方法名 | 功能描述 |
|---|---|
add(int index, E element) | 在指定位置插入元素 |
get(int index) | 获取指定位置的元素 |
indexOf() | 获取元素的索引位置 |
remove(int index) | 删除指定位置的元素 |
set(int index, E element) | 替换指定位置的元素 |
subList(int fromIndex, int toIndex) | 获取子列表 |
List接口实现类
6.1 ArrayList
- 数据结构:数组
- 特点:查询快,增删慢
- 实现:实现了List接口,特有方法不多,方法摘要可参考List接口
6.2 LinkedList
- 数据结构:链表
- 特点:查询慢,增删快
LinkedList特有方法:
// 早期方法
addFirst(); // 在开头添加元素
addLast(); // 在末尾添加元素
getFirst(); // 获取第一个元素
getLast(); // 获取最后一个元素
removeFirst(); // 删除第一个元素
removeLast(); // 删除最后一个元素
// JDK 1.6以后的新方法(推荐使用)
offerFirst(); // 在开头添加元素
offerLast(); // 在末尾添加元素
peekFirst(); // 查看第一个元素
peekLast(); // 查看最后一个元素
pollFirst(); // 删除并返回第一个元素
pollLast(); // 删除并返回最后一个元素
6.3 Vector(已过时)
- 数据结构:数组
- 特点:线程安全,效率低,已被ArrayList代替
- 特有方法:
elements()- 获取Vector的元素,返回枚举类型对象
7. Set接口详解
Set特点
- 无序集合:没有索引位置
- 无重复元素:不允许存储重复元素
- 方法与Collection一致:Set接口没有特有方法,与Collection接口方法一致
Set接口实现类
7.1 HashSet
特点:
- 底层数据结构:哈希表
- 无序存入:元素排列方式与存入顺序无关
- 内部有序:集合内部按照对象的哈希值排列
元素重复判断机制:
- 首先比较哈希值
- 如果哈希值相等,再调用equals()方法比较对象内容
- 如果哈希值不相等,直接判定为不同对象
自定义类在HashSet中的使用示例:
public class Person {
private String name;
private int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
// 重写hashCode方法
@Override
public int hashCode() {
// 返回的哈希值主要依据对象属性信息
// 尽量避免对象属性值的相同
return name.hashCode() + age;
}
// 重写equals方法
@Override
public boolean equals(Object obj) {
// 如果对象不属于Person类型,返回false
if (!(obj instanceof Person)) {
return false;
} else {
// 将对象强转为Person类型,并比较两个属性
Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age;
}
}
// 此处省略Getters and Setters方法
}
7.2 TreeSet
特点:
- 底层数据结构:二叉树
- 存入无序,内部有序:存入时无序,但内部按照自然顺序排序
- 自动排序:存入元素时会进行自然顺序对比
TreeSet的排序机制
方式一:自然排序(Comparable接口)
原理:
- Java中的
Comparable接口强行对实现它的每个类的对象进行整体排序 - 此排序被称为该类的自然排序
- 类的
compareTo方法被称为自然比较方法
自定义Student类实现Comparable接口示例:
public class Student implements Comparable {
private String name;
private int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
// 重写compareTo方法
@Override
public int compareTo(Object obj) {
// 如果比较对象不是Student类型,无比较意义
if (!(obj instanceof Student)) {
throw new RuntimeException("比较对象不是Student对象");
}
Student s = (Student) obj;
// 对象按照age的自然顺序来排序
if (this.age > s.age) {
return 1; // 返回正整数代表此对象大于比较对象
}
// 如果age值相等,按照name的字符串自然顺序排序
if (this.age == s.age) {
return this.name.compareTo(s.name);
} else {
return -1; // 返回负整数代表此对象小于比较对象
}
}
// 此处省略Student类变量的Getters and Setters方法
}
⚠️ 注意: compareTo方法返回0时,系统判定比较的两个对象为相同元素,不会将元素存入集合中。
扩展技巧: 如果将compareTo方法的返回值固定为正整数或负整数,TreeSet集合就成了一个链表结构的Set集合。
方式二:比较器排序(Comparator接口)
使用场景:
- 元素自身不具备比较性,没实现Comparable接口
- 具备的比较性不是我们所需要的
实现步骤:
- 实现
Comparator接口 - 重写
compare(T o1, T o2)方法 - 将比较器传入TreeSet的构造方法中
构造方法:
TreeSet(Comparator<? super E> comparator)
定义比较器示例:
// 1. 实现Comparator接口
// 2. 重写compare(T o1, T o2)方法
// 3. 重写equals方法(可选)
8. 两个排序接口对比
| 接口 | 包位置 | 方法 | 用途 |
|---|---|---|---|
Comparable | java.lang | compareTo() | 强行对类的对象进行自然排序 |
Comparator | java.util | compare() | 比较函数强行对某些对象集合进行整体排序 |
元素判重机制:
- HashSet:根据
hashCode和equals方法判断元素是否相同 - TreeSet:根据
compareTo的返回值是否为0判断元素是否相同
9. Map接口详解
Map特点
- 键值对存储:存储键值对元素
- 键的唯一性:需要保证键的唯一性
- 无序集合:没有角标位置
- 支持键排序:可以使用键的自然顺序排序,并按照键获取值
Map主要方法
| 方法名 | 功能描述 |
|---|---|
put(K key, V value) | 添加键值对,返回原值 |
putAll(Map<? extends K, ? extends V> m) | 添加一组键值对 |
clear() | 移除所有映射关系 |
containsKey(Object key) | 判断是否包含指定键 |
get(Object key) | 根据键获取值,若此键无元素返回null |
hashCode() | 返回哈希码值 |
isEmpty() | 判断是否为空 |
equals() | 判断是否相等 |
remove(Object key) | 按照键删除映射关系 |
size() | 返回键值对数量 |
values() | 获取所有值,返回Collection类型 |
keySet() | 返回Set集合,将所有键存入Set集合中 |
entrySet() | 返回Map关系集合,Map关系是Map.Entry类型对象 |
Map.Entry接口
描述: Map中的一个内部接口,表示映射项(键值对)
获取方式: 通过Map中的entrySet()方法获取
主要方法:
| 方法名 | 功能描述 |
|---|---|
getKey() | 获取键 |
getValue() | 获取值 |
setValue(V value) | 设置值 |
hashCode() | 返回哈希码 |
equals(Object o) | 判断相等 |
Map接口实现类
9.1 HashMap
- 数据结构:哈希表
- 线程安全:线程不同步
- 空值支持:允许使用空键或空值
- 有序性:无序集合,但内部按照键的哈希值排序
9.2 TreeMap
- 数据结构:二叉树
- 有序性:无序集合,内部按照键的二叉树结构排序
- 排序控制:可以改变键的自然顺序来改变集合中元素的顺序
9.3 Hashtable(不推荐)
- 数据结构:哈希表
- 线程安全:线程同步
- 空值支持:不可以存入空键或空值
- 历史地位:JDK1.2之前存在,现在一般选择HashMap
💡 提示: HashMap和TreeMap最常用的方法是entrySet()。
10. Collections工具类
Collections是专门对集合进行操作的工具类,只包含静态方法。
常用方法
| 方法名 | 功能描述 |
|---|---|
sort() | 对集合进行内部排序,一般用于List集合 |
max() | 返回最大元素 |
min() | 返回最小元素 |
binarySearch(List list, T key) | 对List集合进行二分搜索 |
copy() | 复制集合 |
fill(List list, T obj) | 替换集合中所有元素 |
replaceAll(List list, T oldVal, T newVal) | 替换指定元素 |
reverse(List list) | 反转集合 |
reverseOrder() | 获取逆向比较器,用于反转自然顺序排列 |
synchronizedCollection(Collection c) | 给指定集合加锁 |
swap(List list, int i, int j) | 交换位置 |
shuffle() | 随机交换位置,重新排列集合 |
11. 容易混淆的类对比
| 类/接口名 | 类型 | 功能描述 |
|---|---|---|
| Collection | 集合接口 | 所有集合的顶层接口 |
| Collections | 工具类 | 操作集合的工具类,包含静态方法 |
| Comparable | 排序接口 | 自然排序接口,实现后重写compareTo方法进行排序 |
| Comparator | 比较器接口 | 比较器接口,包含compare和equals方法,构造具有自定义比较器的集合 |
| Collator | 抽象类 | Comparator的子类,抽象类 |
12. 集合选择指南
根据需求选择集合类型
需要保证元素顺序且允许重复:
- 查询多,增删少 →
ArrayList - 增删多,查询少 →
LinkedList
需要保证元素唯一且不关心顺序:
- 无排序需求 →
HashSet - 需要排序 →
TreeSet
需要键值对存储:
- 无排序需求 →
HashMap - 需要按键排序 →
TreeMap
性能对比表
| 集合类型 | 查询 | 插入 | 删除 | 线程安全 |
|---|---|---|---|---|
| ArrayList | 快 | 慢 | 慢 | 否 |
| LinkedList | 慢 | 快 | 快 | 否 |
| Vector | 快 | 慢 | 慢 | 是 |
| HashSet | 快 | 快 | 快 | 否 |
| TreeSet | 较快 | 较慢 | 较慢 | 否 |
| HashMap | 快 | 快 | 快 | 否 |
| TreeMap | 较快 | 较慢 | 较慢 | 否 |
| Hashtable | 快 | 快 | 快 | 是 |
总结
Java集合框架为我们提供了丰富的数据结构选择:
-
Collection系列:用于存储单个元素
- List:有序可重复
- Set:无序不重复
-
Map系列:用于存储键值对
- 根据是否需要排序选择HashMap或TreeMap
-
选择原则:
- 根据数据特点选择合适的集合类型
- 考虑线程安全需求
- 权衡查询和修改操作的频率
-
工具支持:
- 使用Collections工具类进行集合操作
- 合理使用Iterator进行遍历
- 正确实现hashCode和equals方法
掌握Java集合框架,能够让我们更高效地处理数据,是Java开发的重要基础技能。
本文详细介绍了Java集合框架的核心概念、主要接口和实现类,以及实际使用中的选择策略,希望对Java学习者有所帮助。