JAVA|集合框架
1. 集合体系结构
1.1 核心概念
- 集合:Java 中专门用于存储、操作一组对象(引用类型)的容器,区别于数组的核心特点是「长度可变」,且提供了大量便捷的操作方法(如添加、删除、查找、排序等),功能比数组更丰富、更灵活。注意:集合不能直接存储基本数据类型,需使用对应的包装类(如 int → Integer、char → Character)。
- 两大核心体系:Java 集合框架的顶层设计分为两大分支,所有集合类均围绕这两大体系实现,核心区别在于存储方式不同:
- 单列集合 Collection:一次只能存储一个元素,是所有单列集合的顶层接口,主要实现分支为 List 和 Set。
- List:存取有序、元素可重复、有索引(下标),可通过索引直接操作元素,适合需要按顺序存储、频繁查询的场景。
- Set:元素不可重复、无索引,存取顺序不一定与插入顺序一致(部分实现类除外),适合需要去重的场景。
- 双列集合 Map:以「键值对(Key-Value)」的形式存储数据,相当于“字典”,通过 Key 快速定位 Value,适合需要通过一个标识查找对应数据的场景(如根据学号找学生信息)。核心规则:键(Key)唯一、值(Value)可重复,一个 Key 只能对应一个 Value。
- 单列集合 Collection:一次只能存储一个元素,是所有单列集合的顶层接口,主要实现分支为 List 和 Set。
2. Collection 单列集合根接口
2.1 概述
Collection是 List、Set 接口的直接父接口,定义了所有单列集合都具备的通用方法,是整个单列集合体系的“规范”。- 它是一个接口,而非具体实现类,因此不能直接通过
new Collection()创建对象,必须使用它的具体实现类(如 ArrayList、HashSet 等)来创建实例。 - Collection 接口的方法具有通用性,无论使用 List 还是 Set 的实现类,都能调用这些方法,降低了代码的耦合度。
2.2 常用方法
| 方法声明 | 功能说明 | 注意事项 |
|---|---|---|
| boolean add(E e) | 向集合中添加一个元素,添加成功返回 true,失败返回 false | List 集合始终返回 true(可重复添加);Set 集合添加重复元素时返回 false |
| boolean remove(Object o) | 删除集合中指定的元素,删除成功返回 true,无此元素返回 false | 删除时依赖元素的 equals() 方法判断是否是目标元素;若集合中有多个相同元素(仅 List 支持),仅删除第一个 |
| void clear() | 清空集合中的所有元素,集合本身仍存在(仅清空内容) | 清空后集合 size() 为 0,注意与“集合对象为 null”区分(null 是对象不存在,clear() 是对象存在但无内容) |
| boolean contains(Object o) | 判断集合中是否包含指定元素,包含返回 true,否则返回 false | 核心依赖元素的 equals() 方法;自定义对象若未重写 equals(),会使用 Object 类的默认实现(比较地址值),导致判断失效 |
| boolean isEmpty() | 判断集合是否为空(无任何元素),为空返回 true,否则返回 false | 与 size() == 0 效果一致,但 isEmpty() 更简洁、高效 |
| int size() | 返回集合中元素的实际个数(有效元素数) | 注意:集合的“容量”(如 ArrayList 的初始容量)与 size() 无关,容量是底层存储的大小,size() 是实际元素数 |
代码示例
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo {
public static void main(String[] args) {
// 创建 Collection 实现类对象(ArrayList)
Collection<String> coll = new ArrayList<>();
// 1. add():添加元素
coll.add("Java");
coll.add("Python");
coll.add("C++");
System.out.println("添加元素后:" + coll); // [Java, Python, C++]
// 2. remove():删除指定元素
boolean isRemoved = coll.remove("Python");
System.out.println("是否删除成功:" + isRemoved); // true
System.out.println("删除后:" + coll); // [Java, C++]
// 3. contains():判断是否包含指定元素
boolean containsJava = coll.contains("Java");
System.out.println("是否包含 Java:" + containsJava); // true
// 4. size():获取元素个数
System.out.println("集合大小:" + coll.size()); // 2
// 5. isEmpty():判断是否为空
System.out.println("是否为空:" + coll.isEmpty()); // false
// 6. clear():清空集合
coll.clear();
System.out.println("清空后:" + coll); // []
System.out.println("清空后是否为空:" + coll.isEmpty()); // true
}
}
2.3 三种遍历方式
2.3.1 迭代器 Iterator
最基础、最安全的遍历方式
// 1. 获取集合的迭代器对象
Iterator<String> it = coll.iterator();
// 2. 循环判断是否有下一个元素
while (it.hasNext()) {
// 3. 获取下一个元素(注意:next() 会移动迭代器指针,不可重复调用)
String s = it.next();
// 遍历中操作元素(如打印)
System.out.println(s);
}
- 核心注意点:遍历过程中,只能使用迭代器自身的 remove() 方法删除元素,不能使用集合的 remove() 方法。
- 易错点:若使用集合的 remove() 方法,会导致迭代器的“预期修改数”与“实际修改数”不一致,抛出
ConcurrentModificationException(并发修改异常)。 - 补充:迭代器的 remove() 方法,会删除当前迭代器指向的元素(即上一次 next() 获取的元素),删除后迭代器指针不会回退。
2.3.2 增强 for 循环
foreach,最简洁的遍历方式
// 格式:for (元素类型 变量名 : 集合对象)
for (String s : coll) {
System.out.println(s);
}
- 底层原理:本质是迭代器的简化写法,底层仍会创建 Iterator 对象,因此遍历中同样不能使用集合的 remove() 方法,否则会抛出并发修改异常。
- 核心易错点:遍历过程中修改变量 s 的值,不会修改集合本身的元素。因为 s 只是集合元素的“副本”,不是元素本身(值传递特性)。
- 适用场景:仅用于“遍历读取”元素,不涉及元素的添加、删除操作。
2.3.3 Lambda 表达式
JDK 8+ 新增,最简洁、灵活的方式
// 格式:集合对象.forEach(元素 -> 遍历操作)
coll.forEach(s -> System.out.println(s));
// 简化写法(若操作是单一方法调用,可使用方法引用)
coll.forEach(System.out::println);
- 优势:代码简洁,无需关注迭代器的细节,可直接通过 Lambda 表达式定义遍历逻辑(如遍历的同时过滤、修改元素)。
- 注意:遍历中若需删除元素,仍需谨慎,建议使用迭代器或 stream 流的 filter 方法,避免并发修改异常。
2.3.4 代码示例
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionTraverseDemo {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<>();
coll.add("张三");
coll.add("李四");
coll.add("王五");
// 方式1:迭代器 Iterator
System.out.println("=== 迭代器遍历 ===");
Iterator<String> it = coll.iterator();
while (it.hasNext()) {
String name = it.next();
System.out.println(name);
// 遍历中删除元素(仅能使用迭代器的 remove())
if (name.equals("李四")) {
it.remove();
}
}
System.out.println("删除李四后:" + coll); // [张三, 王五]
// 重新添加元素,用于后续遍历
coll.add("李四");
// 方式2:增强 for 循环
System.out.println("=== 增强 for 遍历 ===");
for (String name : coll) {
System.out.println(name);
}
// 方式3:Lambda 表达式
System.out.println("=== Lambda 遍历 ===");
// 本质是匿名内部类
coll.forEach(new Consumer<String>() {
@Override
public void accept(String name) {
System.out.println("姓名:" + name);
}
});
// Lambda表达式
coll.forEach(name -> System.out.println("姓名:" + name));
}
}
3. List 集合
List集合添加的元素是有序、可重复、有索引的。
3.1 核心特点
- 存取有序:元素的插入顺序与遍历顺序一致(插入时按顺序存储,遍历时分顺序取出)。
- 元素可重复:允许添加多个值相等的元素(判断相等依赖 equals() 方法)。
- 有索引:元素有对应的下标(从 0 开始,与数组一致),可通过索引直接获取、修改、删除元素,这是 List 与 Set 最核心的区别。
- 常用实现类:ArrayList(最常用)、LinkedList、Vector(线程安全,效率低,已过时)。
3.2 特有方法
| 方法声明 | 功能说明 | 易错点 |
|---|---|---|
| void add(int index, E e) | 在指定索引位置插入元素,原索引及后续元素依次后移(扩容) | 索引不能越界(index 需满足 0 ≤ index ≤ size()),否则抛出 IndexOutOfBoundsException |
| E remove(int index) | 删除指定索引位置的元素,返回被删除的元素,后续元素依次前移 | 索引越界会报错;与 remove(Object o) 区分:前者按索引删,后者按元素删 |
| E set(int index, E e) | 修改指定索引位置的元素,返回修改前的旧元素 | 仅修改元素值,不改变集合长度;索引越界报错 |
| E get(int index) | 获取指定索引位置的元素,返回该元素 | 索引越界报错;这是 List 最常用的方法,用于快速查询元素 |
代码示例
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
// 1. add(int index, E e):指定索引插入元素
list.add(1, "X");
System.out.println("指定索引插入后:" + list); // [A, X, B, C]
// 2. remove(int index):删除指定索引元素
String removed = list.remove(1);
System.out.println("删除的元素:" + removed); // X
System.out.println("删除后:" + list); // [A, B, C]
// 3. set(int index, E e):修改指定索引元素
String old = list.set(2, "D");
System.out.println("修改前的旧元素:" + old); // C
System.out.println("修改后:" + list); // [A, B, D]
// 4. get(int index):获取指定索引元素
String element = list.get(1);
System.out.println("索引1的元素:" + element); // B
}
}
3.3 五种遍历方式
| 遍历方式 | 核心特点 | 适用场景 | 注意事项 |
|---|---|---|---|
| 普通 for 循环 | 按索引遍历,可手动控制索引,支持增删改 | 需要操作索引、遍历中修改元素 | 正向删除元素会漏删(需 i–);索引越界风险 |
| 增强 for 循环 | 简洁,无需关注索引 | 仅读取元素,不修改集合 | 遍历中删除元素会抛并发修改异常 |
| 迭代器 Iterator | 安全遍历,支持遍历中删除 | 遍历中需要删除元素 | 必须先调用 hasNext (),再调用 next () |
| Lambda 表达式 | 极简语法(JDK8+),函数式编程 | 简单遍历、快速处理元素 | 遍历中删除元素需谨慎(推荐迭代器) |
| ListIterator | 迭代器升级版,支持双向遍历、添加 / 修改元素 | 双向遍历、遍历中添加 / 修改元素 | 仅 List 特有,Set/Map 不支持 |
3.3.1 普通 for 循环
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
// 正向遍历
for (int i = 0; i < list.size(); i++) {
String element = list.get(i);
System.out.println("索引" + i + ":" + element);
}
// 反向遍历
for (int i = list.size() - 1; i >= 0; i--) {
System.out.println(list.get(i));
}
3.3.2 增强 for 循环
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
for (String element : list) {
System.out.println(element);
}
3.3.3 迭代器 Iterator
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String element = it.next();
if ("B".equals(element)) {
it.remove(); // 遍历中删除元素,唯一安全方式
}
System.out.println(element);
}
3.3.4 Lambda 表达式
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list.forEach(element -> System.out.println(element));
3.3.5 ListIterator(List 特有迭代器)
| 常用方法 | 功能说明 |
|---|---|
boolean hasNext() | 判断是否有下一个元素(正向遍历) |
E next() | 获取下一个元素,指针后移 |
int nextIndex() | 获取下一个元素的索引(正向遍历) |
boolean hasPrevious() | 判断是否有上一个元素(反向遍历) |
E previous() | 获取上一个元素,指针前移 |
int previousIndex() | 获取上一个元素的索引(反向遍历) |
void remove() | 删除当前迭代器指向的元素(与 Iterator 一致) |
void set(E e) | 修改当前迭代器指向的元素(替换上一次 next ()/previous () 获取的元素) |
void add(E e) | 在当前指针位置插入元素(插入后元素在 next () 位置,previous () 位置前) |
ListIterator 是 List 专属迭代器,相比普通 Iterator,新增了boolean hasPrevious()、E previous()、int previousIndex()方法,可以将迭代其中的指针向前移动,实现反向遍历列表。
代码示例
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
ListIterator<String> lit = list.listIterator();
// 正向遍历
while (lit.hasNext()) {
System.out.println(lit.next());
}
// 反向遍历(先正向走到末尾)
while (lit.hasPrevious()) {
System.out.println(lit.previous());
}
// 遍历中添加/修改元素
lit = list.listIterator();
while (lit.hasNext()) {
String element = lit.next();
if ("B".equals(element)) {
lit.set("B-modified"); // 修改当前元素
lit.add("D"); // 在当前元素后添加新元素
}
}
System.out.println(list); // 输出 [A, B-modified, D, C]
3.3.6 五种遍历方式的选择
-
只读遍历:优先选 增强 for 循环 或 Lambda 表达式(简洁);
-
遍历中删除元素:必须用 Iterator(通用)或 ListIterator(List 特有);
-
需要操作索引 / 反向遍历:用 普通 for 循环;
-
遍历中添加 / 修改元素:用 ListIterator(List 专属);
所有遍历方式中,增强 for/Lambda 仅适合 “只读”,修改 / 删除需选迭代器或普通 for 循环。
3.4 两个核心实现类
ArrayList 和 LinkedList 都是 Java 中 List 接口的核心实现类。
3.4.1 ArrayList
- 底层结构:动态数组(底层维护一个数组,默认初始容量为 0,第一次添加元素时扩容为 10)。
- 核心特性:
- 查询快:数组支持通过索引直接定位元素,时间复杂度为 O(1)(无需遍历)。
- 增删慢:增删元素时,需要移动后续元素(如在中间插入元素,后续所有元素都要后移),时间复杂度为 O(n)(n 为元素个数)。
- 扩容机制:
- 初始状态:空数组(容量 0)。
- 第一次添加元素:扩容为 10。
- 后续扩容:当数组满了(size() == 容量),按1.5 倍扩容(如 10 → 15 → 22 → …)。
- 补充:可通过 ArrayList(int initialCapacity) 构造方法指定初始容量,避免频繁扩容(如已知集合会存储 100 个元素,直接指定初始容量为 100,提升效率)。
- 适用场景:频繁查询、少量增删的场景(如展示列表、数据查询结果存储)。
3.4.2 LinkedList
-
底层结构:双向链表(每个元素都有前驱节点和后继节点,形成链表结构,无需连续的内存空间)。
-
核心特性:
- 查询慢:链表没有索引,查询指定元素时,需要从表头或表尾开始遍历,时间复杂度为 O(n)。
- 增删快:增删元素时,只需修改前后节点的指针,无需移动其他元素,时间复杂度为 O(1)(首尾操作最优)。
-
特有首尾操作方法:
特有方法 说明 void addFirst(E e) 在链表头部添加元素(等价于 push()) void addLast(E e) 在链表尾部添加元素(等价于 add()) E getFirst() 获取头部元素,若集合为空,抛出 NoSuchElementException E getLast() 获取尾部元素,若集合为空,抛出 NoSuchElementException E removeFirst() 删除头部元素,返回被删除元素 E removeLast() 删除尾部元素,返回被删除元素 -
适用场景:频繁在首尾增删元素的场景(如队列、栈的实现)。
4. Set 集合
Set集合添加的元素是无序、不可重复、无索引的。
4.1 核心特点
- 元素不可重复:集合中不会出现两个 equals() 方法返回 true 的元素(去重特性)。
- 无索引:没有下标,不能通过索引操作元素,因此不能使用普通 for 循环遍历(只能用迭代器、增强 for、Lambda)。
- 存取顺序:默认无序(部分实现类除外),即插入顺序与遍历顺序不一定一致。
- 核心原理:去重机制(不同实现类的去重方式不同,是重点)。
- 常用实现类:HashSet(最常用)、LinkedHashSet、TreeSet。
4.2 三个核心实现类
4.2.1 HashSet
-
底层结构:哈希表(JDK 8 前为「数组 + 链表」的组合结构JDK 8 后为「数组 + 链表 + 红黑树」的组合结构),哈希表的核心是“哈希值”。
哈希值
含义:
对象的整数表现形式
概述:
-
根据hashCode方法算出来的int类型的整数
-
该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
-
一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值
对象的哈希值特点:
-
如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
-
如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
-
在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。(哈希碰撞)
-
-
核心特性:无序、去重、效率高(添加、删除、查询的时间复杂度接近 O(1))。
-
去重原理:依赖元素的
hashCode()和equals()两个方法,执行流程如下:- 当向 HashSet 中添加元素时,先调用该元素的 hashCode() 方法,获取其哈希值。
- 根据哈希值,计算出该元素在哈希表中的存储位置(公式:
(数组长度-1) & 哈希值)。 - 判断该位置是否有元素:
- 若没有元素,直接存入该位置。
- 若有元素,调用该元素的 equals() 方法,与当前要添加的元素比较:
- equals() 返回 true:视为同一个元素,不添加。
- equals() 返回 false:视为不同元素,存入该位置(形成链表,当链表长度超过 8 并且数组长度超过 64 时,转为红黑树,提升查询效率)。
-
注意:自定义对象作为 HashSet 的元素时,必须同时重写 hashCode() 和 equals() 方法,否则会导致去重失效(默认使用 Object 类的方法,比较地址值,即使内容相同,也会视为不同元素)。
-
补充:HashSet遍历按数组 + 链表 / 红黑树顺序进行,因此遍历顺序与插入顺序不一致。
4.2.2 LinkedHashSet
- 底层结构:哈希表 + 双向链表(在 HashSet 的基础上,增加了一条双向链表,用于记录元素的插入顺序)。
- 核心特性:有序(存取顺序一致)、去重,效率略低于 HashSet(因为多了链表维护顺序)。
- 去重原理:与 HashSet 完全一致(依赖 hashCode() 和 equals()),只是额外维护了插入顺序,适合需要“去重且保留插入顺序”的场景。
4.2.3 TreeSet
- 底层结构:红黑树(一种平衡二叉搜索树,具有自动排序功能)。
- 核心特性:可排序(默认升序)、去重,效率低于 HashSet 和 LinkedHashSet。
- 排序方式:
- 自然排序(默认):让元素所属的类实现
Comparable接口,并重写compareTo(T o)方法,定义元素的排序规则。- compareTo() 方法返回值规则:
- 返回负数:当前元素 < 比较元素,当前元素排在前面。
- 返回 0:当前元素 == 比较元素(视为同一个元素,去重)。
- 返回正数:当前元素 > 比较元素,当前元素排在后面。
- Integer、String 等内置类,已实现 Comparable 接口,默认按升序排序。
- compareTo() 方法返回值规则:
- 比较器排序(自定义排序):创建 TreeSet 时,传入
Comparator接口的实现类(或 Lambda 表达式),自定义排序规则,优先级高于自然排序。
- 自然排序(默认):让元素所属的类实现
- 去重原理:基于排序规则,若两个元素通过排序方法(compareTo 或 compare)返回 0,视为同一个元素,不添加。
- 适用场景:需要对元素进行排序且去重的场景(如排行榜、排序后的去重数据)。
5 Map 双列集合
键值对存储
5.1 概述
- 存储格式:以「键值对(Key-Value)」为单位存储数据,Key 和 Value 都是引用类型,一个 Key 对应一个 Value,可理解为“键是唯一标识,值是对应的数据”。
- 核心规则:
- 键(Key)唯一:不能有两个相同的 Key(判断标准与 HashSet 一致,依赖 hashCode() 和 equals())。
- 值(Value)可重复:多个 Key 可以对应同一个 Value。
- 键可以为 null,但最多只能有一个 null 键(因为 Key 唯一);值可以为 null,且可以有多个 null 值。
- 顶层接口:Map 是双列集合的顶层接口,不能直接创建对象,需使用其实现类。
- 与 Collection 的区别:Collection 是单列存储,Map 是双列存储;Map 不能直接遍历,需先转换为 Collection 再遍历。
5.2 常用方法
| 方法声明 | 功能说明 | 注意事项 |
|---|---|---|
| V put(K key, V value) | 添加键值对;若 Key 已存在,会覆盖原有的 Value,返回被覆盖的旧 Value;若 Key 不存在,返回 null | Key 的唯一性由 hashCode () 和 equals () 保证,自定义 Key 需重写这两个方法 |
| V remove(Object key) | 根据 Key 删除对应的键值对,返回被删除的 Value;若 Key 不存在,返回 null | 删除的是整个键值对,不是单独的 Key 或 Value;JDK 8+ 新增重载方法 remove (Object key, Object value),仅当 Key-Value 完全匹配时才删除 |
| boolean containsKey(Object key) | 判断 Map 中是否包含指定的 Key,包含返回 true,否则返回 false | 判断依据是 Key 的 hashCode () 和 equals () 方法;底层基于哈希表查找,效率为 O (1),推荐优先使用 |
| boolean containsValue(Object value) | 判断 Map 中是否包含指定的 Value,包含返回 true,否则返回 false | 需遍历所有 Value 逐一对比,效率为 O (n),数据量大时慎用;判断依据是 Value 的 equals () 方法 |
| void clear() | 清空 Map 中所有键值对,清空后 Map 仍存在但无任何元素 | 仅清空元素,不会销毁 Map 对象;若需彻底废弃 Map,可额外赋值为 null |
| int size() | 返回 Map 中键值对的个数 | 与 List、Set 的 size () 含义一致,均为有效元素(键值对)个数;时间复杂度为 O (1) |
| boolean isEmpty() | 判断 Map 是否为空(键值对个数为 0),为空返回 true,否则返回 false | 语义上等价于 size() == 0,但可读性更高;时间复杂度为 O (1),推荐优先使用 |
| V get(Object key) | 根据 Key 获取对应的 Value;若 Key 不存在,返回 null | 若 Value 本身可能为 null,无法通过返回值判断 Key 是否存在,需配合 containsKey() 方法 |
| Set keySet() | 返回 Map 中所有 Key 组成的 Set 集合 | 用于“键找值”遍历方式,返回的 Set 集合具有去重特性(因为 Key 唯一) |
| Set<Map.Entry<K,V>> entrySet() | 返回 Map 中所有键值对(Entry 对象)组成的 Set 集合 | Entry 是 Map 的内部接口,封装了 Key 和 Value,提供 getKey() 和 getValue() 方法 |
5.3 三种遍历方式
5.3.1 键找值(最基础、最常用)
// 1. 获取所有 Key 组成的 Set 集合
Set<String> keys = map.keySet();
// 2. 遍历 Key 集合,通过 Key 获取对应的 Value
for (String key : keys) {
String value = map.get(key);
System.out.println(key + "=" + value);
}
- 优势:逻辑简单,适合只需要 Key 和 Value 简单关联的场景。
5.3.2 键值对对象 Entry(最高效)
// 1. 获取所有键值对(Entry 对象)组成的 Set 集合
Set<Map.Entry<String, String>> entries = map.entrySet();
// 2. 遍历 Entry 集合,直接获取 Key 和 Value
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "=" + value);
}
- 优势:一次遍历即可获取 Key 和 Value,无需多次调用 get() 方法,效率高于“键找值”方式。
- 补充:Entry 接口的常用方法:getKey()(获取键)、getValue()(获取值)、setValue(V value)(修改值)。
5.3.3 Lambda 表达式(JDK 8+ 新增,最简洁)
// 底层原理:利用第二种方法进行遍历,依次获取键和值,再调用accept方法
// 匿名内部类
m.forEach(new BiConsumer<String, String>() {
@Override
public void accept(String key, String value) {
System.out.println(key + "=" + value);
}
});
// Lambda表达式
m.forEach((String key, String value) -> System.out.println(key + "=" + value));
- 优势:代码简洁,无需关注 Key 或 Entry 的集合转换,直接定义遍历逻辑。
- 适用场景:快速遍历、简单操作(如打印、过滤),无需复杂的逻辑处理。
5.4 Map 核心实现类
5.4.1 HashMap
- 底层结构:与 HashSet 一致,哈希表(数组 + 链表 + 红黑树),Key 的存储逻辑与 HashSet 完全相同。
- 核心特性:无序、不重复、无索引(特点都是有key决定的)。
- 注意:自定义对象作为 Key 时,必须重写 hashCode() 和 equals() 方法,否则会导致 Key 重复、查找失效。
5.4.2 LinkedHashMap
- 底层结构:哈希表 + 双向链表(在 HashMap 的基础上,增加双向链表维护键值对的插入顺序)。
- 核心特性:有序(存取顺序一致)、Key 唯一、线程不安全,效率略低于 HashMap。
- 适用场景:需要保留键值对插入顺序的场景(如缓存、有序的配置映射)。
5.4.3 TreeMap
- 底层结构:红黑树(与 TreeSet 一致),Key 会按指定规则排序。
- 核心特性:按键排序(默认升序)、Key 唯一、线程不安全,效率低于 HashMap。
- 排序方式:与 TreeSet 完全一致,支持自然排序(Key 实现 Comparable)和比较器排序(传入 Comparator)。
- 适用场景:需要对 Key 进行排序的场景(如按日期排序的缓存、有序的字典映射)。
6. Collections 工具类
- 核心定义:
java.util.Collections是一个工具类(不是集合),提供了大量静态方法,用于操作 Collection 集合和 Map 集合,简化集合的常用操作。 - 注意:Collections 与 Collection 区别:Collection 是集合顶层接口,Collections 是操作集合的工具类。
- 常用静态方法:
| 方法声明 | 功能说明 | 核心注意事项 |
|---|---|---|
addAll(Collection<T> c, T... elements) | 批量向集合中添加多个元素 | 支持可变参数;返回 boolean 表示是否修改集合 |
shuffle(List<?> list) | 随机打乱 List 中元素的顺序 | 结果不可预测;仅适用于 List,常用于随机抽样 / 洗牌场景 |
sort(List<T> list) | 对 List 进行自然排序(升序) | 元素必须实现 Comparable 接口(如 Integer、String);仅适用于 List |
sort(List<T> list, Comparator<T> c) | 按自定义规则对 List 排序 | 可通过 Comparator 实现降序 / 多字段排序;仅适用于 List |
binarySearch(List<T> list, T key) | 二分查找 List 中的指定元素 | 前提:List 必须先排序;找到返回索引,未找到返回负数 |
max(Collection<T> coll) | 获取集合中的最大值(自然排序) | 元素需实现 Comparable;适用于 List/Set 等 Collection 子类 |
min(Collection<T> coll) | 获取集合中的最小值(自然排序) | 元素需实现 Comparable;适用于 List/Set 等 Collection 子类 |
copy(List<T> dest, List<T> src) | 将源 List 的元素复制到目标 List 中 | 目标 List 容量必须 ≥ 源 List;否则抛出 IndexOutOfBoundsException |
fill(List<T> list, T obj) | 用指定元素覆盖 List 中所有原有元素 | 会清空原有数据;仅适用于 List |
swap(List<?> list, int i, int j) | 交换 List 中指定索引 i 和 j 位置的元素 | 索引从 0 开始;超出范围抛出 IndexOutOfBoundsException;仅适用于 List |
- 代码示例
public class CollectionsDemo1 {
public static void main(String[] args) {
System.out.println("-------------addAll--------------------------");
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a","b","c","d","e","f","g","h");
System.out.println(list);
System.out.println("-------------shuffle--------------------------");
Collections.shuffle(list);
System.out.println(list);
System.out.println("-------------sort默认规则--------------------------");
//默认规则,需要重写Comparable接口compareTo方法。Integer已经实现,按照从小打大的顺序排列
//如果是自定义对象,需要自己指定规则
ArrayList<Integer> list1 = new ArrayList<>();
Collections.addAll(list1, 10, 1, 2, 4, 8, 5, 9, 6, 7, 3);
Collections.sort(list1);
System.out.println(list1);
System.out.println("-------------sort自己指定规则规则--------------------------");
Collections.sort(list1, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
System.out.println(list1);
Collections.sort(list1, (o1, o2) -> o2 - o1);
System.out.println(list1);
System.out.println("-------------binarySearch--------------------------");
//需要元素有序
ArrayList<Integer> list2 = new ArrayList<>();
Collections.addAll(list2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(Collections.binarySearch(list2, 9));
System.out.println(Collections.binarySearch(list2, 1));
System.out.println(Collections.binarySearch(list2, 20));
System.out.println("-------------copy--------------------------");
//把list3中的元素拷贝到list4中
//会覆盖原来的元素
//注意点:如果list3的长度 > list4的长度,方法会报错
ArrayList<Integer> list3 = new ArrayList<>();
ArrayList<Integer> list4 = new ArrayList<>();
Collections.addAll(list3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Collections.addAll(list4, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
Collections.copy(list4, list3);
System.out.println(list3);
System.out.println(list4);
System.out.println("-------------fill--------------------------");
//把集合中现有的所有数据,都修改为指定数据
ArrayList<Integer> list5 = new ArrayList<>();
Collections.addAll(list5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Collections.fill(list5, 100);
System.out.println(list5);
System.out.println("-------------max/min--------------------------");
//求最大值或者最小值
ArrayList<Integer> list6 = new ArrayList<>();
Collections.addAll(list6, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(Collections.max(list6));
System.out.println(Collections.min(list6));
System.out.println("-------------max/min指定规则--------------------------");
// String中默认是按照字母的abcdefg顺序进行排列的
// 现在我要求最长的字符串
// 默认的规则无法满足,可以自己指定规则
// 求指定规则的最大值或者最小值
ArrayList<String> list7 = new ArrayList<>();
Collections.addAll(list7, "a","aa","aaa","aaaa");
System.out.println(Collections.max(list7, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
}));
System.out.println("-------------swap--------------------------");
ArrayList<Integer> list8 = new ArrayList<>();
Collections.addAll(list8, 1, 2, 3);
Collections.swap(list8,0,2);
System.out.println(list8);
}
}
更多推荐



所有评论(0)