背景
本文是《Java 后端从小白到大神》修仙系列第十四篇,正式进入Java后端世界,本篇文章主要聊Java基础。若想详细学习请点击首篇博文,我们开始吧。
文章概览
- Arrays 类 - 数组操作工具
- Collections 类 - 集合操作工具
- Objects 类 - 对象安全操作
- Files/Paths 类 - NIO 文件操作
- Math 类 - 数学计算工具
- Stream API - 函数式数据处理
- StringUtils - Apache 字符串工具
一、Arrays 类
1.1 核心定位
Arrays 是 Java 提供的数组工具类,包含操作数组的静态方法。理解其内部实现有助于写出高性能代码。
1.2 常用方法详解
| 方法 |
时间复杂度 |
空间复杂度 |
核心用途 |
sort() |
O(n log n) |
O(log n) |
双轴快排(基本类型)/ TimSort(对象) |
binarySearch() |
O(log n) |
O(1) |
二分查找(需先排序) |
copyOf() |
O(n) |
O(n) |
数组扩容/缩容 |
fill() |
O(n) |
O(1) |
批量填充默认值 |
equals() |
O(n) |
O(1) |
深度比较数组内容 |
asList() |
O(1) |
O(1) |
数组转固定大小 List |
1.3 代码示例与原理分析
- 数组排序
1
2
3
|
int[] arr = {5, 3, 1, 4, 2};
Arrays.sort(arr); // 升序排序
// 结果:[1, 2, 3, 4, 5]
|
- 二分查找(必须先排序)
1
2
3
4
5
|
int[] arr = {1, 2, 3, 4, 5};
int index = Arrays.binarySearch(arr, 4); // 找到:返回下标 3
// 返回 = -(插入点) - 1
// 现在要找 6,它应该插入在最后面,插入点下标是 5,所以返回 -6
int notFound = Arrays.binarySearch(arr, 6); // 未找到:返回负数 -6
|
- 数组拷贝
1
2
3
4
5
|
int[] arr = {1, 2, 3, 4, 5};
int[] expanded = Arrays.copyOf(arr, 8); // 扩容:[1,2,3,4,5,0,0,0]
int[] truncated = Arrays.copyOf(arr, 3); // 缩容:[1,2,3]
int[] rangeCopy = Arrays.copyOfRange(arr,1,4); // 范围拷贝:[2,3,4]
|
- 数组转 List(重要陷阱)
1
2
3
4
5
6
|
// 固定大小 List,不能 add/remove
List<Integer> list = Arrays.asList(1, 2, 3);
// 正确:转成可修改 ArrayList
List<Integer> mutableList = new ArrayList<>(Arrays.asList(1,2,3));
mutableList.add(4); // 结果:[1,2,3,4]
|
- 多维数组操作
1
2
3
4
5
6
7
8
|
int[][] matrix = {{1,2},{3,4}};
int[][] matrix2 = {{1,2},{3,4}};
// 打印嵌套数组
Arrays.deepToString(matrix); // [[1, 2], [3, 4]]
// 深度比较多维数组
Arrays.deepEquals(matrix, matrix2); // true
|
- 数组 Stream 常用操作
1
2
3
4
5
|
int[] arr = {1,2,3,4,5};
int sum = Arrays.stream(arr).sum(); // 总和:15
int max = Arrays.stream(arr).max().orElse(0); // 最大值:5
double avg = Arrays.stream(arr).average().orElse(0); // 平均值:3.0
|
1.4 性能陷阱与最佳实践
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ArraysBestPractice {
// 陷阱1:频繁扩容,性能极差 O(n²)
public void badPractice() {
int[] arr = new int[0];
for (int i = 0; i < 1000; i++) {
// 每次都扩容复制,效率极低
arr = Arrays.copyOf(arr, arr.length + 1);
arr[i] = i;
}
}
// 正确做法1:一次性指定容量
public void goodPractice() {
// 直接分配足够空间,一次搞定
int[] arr = new int[1000];
for (int i = 0; i < 1000; i++) {
arr[i] = i;
}
}
// 陷阱2:Arrays.asList 与原数组互相影响,且不能增删
public void asListTrap() {
String[] array = {"a", "b", "c"};
List<String> list = Arrays.asList(array);
// 修改原数组
array[0] = "x";
// List 也跟着变,因为底层共用同一个数组
System.out.println("陷阱2结果:" + list.get(0)); // 输出 x
}
// 正确做法2:转成独立的 ArrayList,数组和List互不影响
public void asListGoodPractice() {
String[] array = {"a", "b", "c"};
// 新建一个真正的 ArrayList,复制数据,完全独立
List<String> list = new ArrayList<>(Arrays.asList(array));
// 修改原数组
array[0] = "x";
// List 不受影响
System.out.println("正确做法结果:" + list.get(0)); // 依然是 a
}
// 运行测试
public static void main(String[] args) {
ArraysBestPractice demo = new ArraysBestPractice();
System.out.println("===== 陷阱2测试 =====");
demo.asListTrap();
System.out.println("===== 正确做法测试 =====");
demo.asListGoodPractice();
}
}
|
二、Collections 类
2.1 核心定位
Collections 提供对集合的静态工具方法,包括排序、查找、同步包装、不可变包装等。
2.2 方法分类
| 类别 |
代表方法 |
用途 |
| 排序 |
sort(), reverse(), shuffle() |
改变元素顺序 |
| 查找 |
binarySearch(), max(), min() |
极值与搜索 |
| 线程安全 |
synchronizedXxx() |
包装线程安全集合 |
| 不可变 |
unmodifiableXxx() |
创建只读视图 |
| 单例 |
singletonXxx() |
创建单元素集合 |
2.3 代码示例
- 排序、反转、打乱
1
2
3
4
5
|
List<Integer> list = new ArrayList<>(Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6));
Collections.sort(list); // [1,1,2,3,4,5,6,9]
Collections.reverse(list); // [9,6,5,4,3,2,1,1]
Collections.shuffle(list); // 随机打乱
|
- 最大值、最小值
1
2
3
4
|
List<Integer> list = Arrays.asList(3,1,4,1,5,9,2,6);
int max = Collections.max(list); // 9
int min = Collections.min(list); // 1
|
- 二分查找(必须先排序)
1
2
3
|
List<Integer> list = Arrays.asList(1,2,3,4,5,6,9);
int index = Collections.binarySearch(list, 5); // 下标 4
|
- 线程安全集合包装
1
2
3
4
|
List<String> unsafeList = new ArrayList<>();
// 包装成线程安全 List(加 synchronized)
List<String> safeList = Collections.synchronizedList(unsafeList);
|
- 不可变(只读)集合
1
2
3
4
|
// 只读,不能 add/remove/set
List<String> readOnly = Collections.unmodifiableList(Arrays.asList("a", "b"));
// readOnly.add("c"); → 抛异常
|
- 单元素集合(内存最优)
1
2
3
|
Set<String> singleSet = Collections.singleton("only");
List<String> singleList = Collections.singletonList("only");
Map<String,Integer> map = Collections.singletonMap("key", 1);
|
- 全部填充为同一个值
1
2
3
|
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
Collections.fill(list, "x"); // [x, x, x]
|
- 批量替换元素
1
2
3
|
List<Integer> list = new ArrayList<>(Arrays.asList(1,2,1,3,1));
Collections.replaceAll(list, 1, 9); // [9,2,9,3,9]
|
- 统计元素出现次数
1
2
3
|
List<Integer> list = Arrays.asList(9,2,9,3,9);
int freq = Collections.frequency(list, 9); // 3
|
- 判断是否无交集
1
2
3
4
5
|
List<String> list1 = Arrays.asList("a", "b", "c");
List<String> list2 = Arrays.asList("b", "c", "d");
// 无交集返回 true,有交集返回 false
boolean disjoint = Collections.disjoint(list1, list2); // false
|
- 集合复制
1
2
3
4
5
6
|
List<String> src = Arrays.asList("a", "b", "c");
// 生成一个包含 3 个空字符串 "" 的不可变集合,结果 = ["", "", ""]
// 作用就是 快速创建一个指定长度、初始值都一样的集合
List<String> dst = new ArrayList<>(Collections.nCopies(3, ""));
// Collections.copy(目标, 来源),dst = 目标(往这里贴),src = 来源(从这里拿)
Collections.copy(dst, src); // dst 变为 [a, b, c]
|
2.4 线程安全 vs 并发集合
- Collections.synchronizedList
1
|
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
|
- 原理:所有方法加
synchronized 锁
- 优点:简单、兼容旧代码
- 缺点:锁粒度粗,并发性能差
- 适用:低并发、简单场景
- CopyOnWriteArrayList(推荐:读多写少)
1
|
List<String> cowList = new CopyOnWriteArrayList<>();
|
- 原理:写时复制新数组,读操作无锁
- 优点:读性能极高,线程安全
- 缺点:写性能低,内存开销大
- 适用:读多写少(白名单、配置、缓存)
- ConcurrentLinkedQueue(高并发队列)
1
|
Queue<String> concurrentQueue = new ConcurrentLinkedQueue<>();
|
- 原理:CAS 无锁算法
- 优点:高并发性能优秀
- 缺点:
size() 慢,需要遍历
- 适用:高并发任务队列、消息队列
三、Objects 类
3.1 核心定位
Objects 类 (空安全工具类),专门做空安全的对象操作,避免空指针异常(NPE),是日常写健壮代码最常用的工具类。
3.2 核心方法
| 方法 |
用途 |
替代方案 |
equals(a, b) |
空安全比较 |
a != null && a.equals(b) |
hashCode(o) |
空安全哈希 |
o != null ? o.hashCode() : 0 |
requireNonNull(o) |
参数校验 |
if (o == null) throw ... |
isNull(o) / nonNull(o) |
空值判断 |
o == null / o != null |
compare(a, b, c) |
空安全比较 |
自定义比较器 |
3.3 代码示例
- Objects.equals(a, b) 空安全相等判断
1
2
3
4
5
6
|
String a = null;
String b = "Hello";
// 安全比较,不会 NPE
boolean eq = Objects.equals(a, b); // false
boolean bothNull = Objects.equals(null, null); // true
|
- 优点:不用自己写
if (a != null) 判空
- 等价安全写法:
a != null && a.equals(b)
- Objects.isNull / nonNull 判断空
1
2
3
4
|
String str = null;
boolean isNull = Objects.isNull(str); // true
boolean nonNull = Objects.nonNull(str); // false
|
常用于 Stream 过滤:
1
2
3
4
5
6
7
|
List<String> list = Arrays.asList("a", null, "b", null);
// 统计 null 数量
long nullCount = list.stream().filter(Objects::isNull).count(); // 2
// 过滤掉 null
List<String> noNull = list.stream().filter(Objects::nonNull).toList();
|
- Objects.requireNonNull 非空校验(最常用)
1
2
3
4
|
String name = null;
// 为空直接抛 NPE
String result = Objects.requireNonNull(name, "用户名不能为空");
|
构造方法里做参数校验:
1
2
3
4
5
6
7
|
class User {
private final String username;
public User(String username) {
this.username = Objects.requireNonNull(username, "username 不能为 null");
}
}
|
- Objects.hashCode / Objects.hash 计算哈希
1
2
3
4
5
6
7
|
String str = "hello";
// 单个对象 hashCode(空时返回 0,不抛异常)
int hash = Objects.hashCode(str);
// 多个字段组合 hashCode(写 hashCode() 方法专用)
int combinedHash = Objects.hash("Tom", 18, "Shanghai");
|
- 排序时处理 null(nullsFirst / nullsLast)
1
2
3
4
5
6
7
8
9
|
List<String> list = Arrays.asList("banana", null, "apple", null);
// null 放前面
list.sort(Comparator.nullsFirst(Comparator.naturalOrder()));
// [null, null, apple, banana]
// null 放后面
list.sort(Comparator.nullsLast(Comparator.naturalOrder()));
// [apple, banana, null, null]
|
- equals() + hashCode() 标准写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
class User {
private String username;
@Override
public boolean equals(Object o) {
// this = 当前正在调用这个方法的那个对象
// this 不是 “类”,是 “当前正在调用 equals 的那个对象”
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(username, user.username);
}
@Override
public int hashCode() {
return Objects.hash(username);
}
}
|
四、Files 与 Paths 类(NIO.2)
4.1 核心定位
Java 7+ 推荐的文件操作工具,完全替代旧的 File 类。优点:简洁、强大、空安全、异常明确、支持大文件、支持流式操作。
4.2 核心方法对比
| 操作 |
传统 IO (java.io.File) |
NIO.2 (Files / Paths) |
| 创建路径对象 |
new File("path") |
Paths.get("path") |
| 读全部字节 |
FileInputStream |
Files.readAllBytes() |
| 读全部文本行 |
BufferedReader |
Files.readAllLines() |
| 写入文件 |
FileOutputStream |
Files.write() |
| 文件复制 |
手动流拷贝 |
Files.copy() |
| 文件移动/改名 |
file.renameTo() |
Files.move() |
| 遍历目录 |
file.listFiles() |
Files.walk() / Files.find() |
| 判断是否存在 |
file.exists() |
Files.exists() |
| 判断是否文件 |
file.isFile() |
Files.isRegularFile() |
| 判断是否目录 |
file.isDirectory() |
Files.isDirectory() |
| 文件大小 |
file.length() |
Files.size() |
| 删除文件 |
file.delete() |
Files.delete() |
| 创建单级目录 |
file.mkdir() |
Files.createDirectory() |
| 创建多级目录 |
递归手动创建 |
Files.createDirectories() |
4.3 代码示例
- 路径操作 Paths.get()
1
2
3
4
5
6
7
8
9
|
// 创建路径对象 /tmp/test/file.txt(不创建文件)
Path path = Paths.get("/tmp", "test", "file.txt");
// 获取父目录 / 文件名 / 根路径
Path parent = path.getParent(); // /tmp/test
Path fileName = path.getFileName(); // file.txt
// 路径拼接 /tmp/test/file.txt
Path resolved = Paths.get("/tmp").resolve("test/file.txt");
|
- 快速读写文件(最常用)
1
2
3
4
5
6
7
8
9
10
11
|
Path path = Paths.get("test.txt");
String content = "Hello NIO.2";
// 写入文件(自动创建,自动关闭)
Files.write(path, content.getBytes());
// 读取文件全部内容 → String
String readContent = Files.readString(path);
// 读取所有行
List<String> lines = Files.readAllLines(path);
|
- 大文件逐行读取(Stream 方式)
1
2
3
4
5
6
7
|
Path path = Paths.get("large.txt");
// 逐行读,不占内存(大文件首选)
try (Stream<String> lines = Files.lines(path)) {
lines.filter(s -> s.contains("Java"))
.forEach(System.out::println);
}
|
- 文件复制 / 移动
1
2
3
4
5
6
7
8
|
Path src = Paths.get("a.txt");
Path dst = Paths.get("b.txt");
// 复制(覆盖已存在)
Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);
// 移动/重命名
Files.move(src, dst);
|
- 目录遍历(walk 递归遍历)
1
2
3
4
5
6
7
8
|
Path dir = Paths.get("/tmp");
// 递归遍历所有文件(深度3层)
try (Stream<Path> stream = Files.walk(dir, 3)) {
stream.filter(Files::isRegularFile) // 只看文件
.filter(p -> p.toString().endsWith(".txt"))
.forEach(System.out::println);
}
|
- 文件属性判断
1
2
3
4
5
|
Path path = Paths.get("test.txt");
boolean exists = Files.exists(path); // 是否存在
boolean isFile = Files.isRegularFile(path); // 是否是文件
long size = Files.size(path); // 文件大小
|
- 创建目录 / 多级目录
1
2
3
4
5
|
// 创建单级目录
Files.createDirectory(Paths.get("/tmp/test"));
// 创建多级目录(自动创建父目录)
Files.createDirectories(Paths.get("/tmp/a/b/c"));
|
- 创建临时文件 / 删除文件
1
2
3
4
5
|
// 创建临时文件
Path temp = Files.createTempFile("demo", ".txt");
// 删除文件
Files.deleteIfExists(temp); // 不存在也不报错
|
- 文件追加写入
1
2
3
|
Path path = Paths.get("test.txt");
Files.write(path, "追加内容".getBytes(), StandardOpenOption.APPEND);
|
4.4 异常处理与资源管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
import java.nio.file.*;
import java.io.IOException;
public class FilesExceptionHandling {
public static void safeFileOperation() {
Path path = Paths.get("/tmp/data.txt");
// 方式1:传统 try-catch
try {
String content = Files.readString(path);
System.out.println(content);
} catch (NoSuchFileException e) {
System.err.println("文件不存在: " + e.getFile());
} catch (AccessDeniedException e) {
System.err.println("权限不足: " + e.getFile());
} catch (IOException e) {
System.err.println("IO 错误: " + e.getMessage());
}
// 方式2:使用 try-with-resources 确保流关闭
try (var lines = Files.lines(path)) {
lines.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
// 方式3:先检查再操作(避免异常)
if (Files.exists(path) && Files.isReadable(path)) {
try {
String content = Files.readString(path);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 原子文件写入(防止写入中断导致文件损坏)
public static void atomicWrite(Path path, String content) throws IOException {
// Files.createTempFile(前缀, 后缀),中间系统自动加随机数字,防止重名,temp + 随机数 + .tmp;例如:/tmp/temp123456789.tmp
Path temp = Files.createTempFile("temp", ".tmp");
try {
Files.writeString(temp, content);
Files.move(temp, path, StandardCopyOption.ATOMIC_MOVE,
StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
Files.deleteIfExists(temp);
throw e;
}
}
}
|
五、Math 类
5.1 核心定位
Java 提供的基础数学运算工具类,包含极值、绝对值、取整、随机数等方法。重点注意:浮点数精度问题、整数溢出陷阱,金融计算必须用 BigDecimal。
5.2 方法分类
| 类别 |
方法 |
说明 |
| 极值 |
max(), min() |
比较大小 |
| 绝对值 |
abs() |
处理负数 |
| 幂运算 |
pow(), sqrt(), cbrt() |
平方、立方根 |
| 取整 |
ceil(), floor(), round() |
向上/向下/四舍五入 |
| 三角函数 |
sin(), cos(), tan() |
弧度制 |
| 随机数 |
random() |
[0.0, 1.0) |
5.3 代码示例
- 基础运算
1
2
3
4
5
6
7
|
int max = Math.max(10, 20); // 20
int min = Math.min(10, 20); // 10
int abs = Math.abs(-10); // 10
double sqrt = Math.sqrt(25); // 5.0
double pow = Math.pow(2, 10); // 1024.0
double cbrt = Math.cbrt(27); // 3.0
|
- 取整运算(重点)
1
2
3
4
5
6
7
8
9
10
|
double num = 3.7;
Math.ceil(num); // 4.0 向上取整
Math.floor(num); // 3.0 向下取整
Math.round(num); // 4 四舍五入(返回 long)
// 负数取整
Math.ceil(-3.7); // -3.0
Math.floor(-3.7); // -4.0
Math.round(-3.7); // -4
|
- 浮点数精度陷阱
1
2
3
4
5
6
7
8
|
// 精度丢失
0.1 + 0.2; // 0.30000000000000004
Math.pow(0.1, 2); // 0.010000000000000002
// 精确计算必须用 BigDecimal
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
a.add(b); // 0.3 完全精确
|
- 绝对值溢出陷阱(重点)
1
2
3
4
5
6
7
8
9
10
|
Math.abs(Integer.MAX_VALUE); // 2147483647 正常
// 溢出陷阱:MIN_VALUE 取绝对值超出 int 范围
Math.abs(Integer.MIN_VALUE); // -2147483648 错误结果
// 安全处理:先转 long 再取绝对值
long safeAbs = Math.abs((long) Integer.MIN_VALUE); // 2147483648
// 安全处理:Integer.absExact,抛异常提示溢出
int safeAbs = Integer.absExact(Integer.MAX_VALUE); // 超出则抛异常提示溢出,否则正常处理
|
- Math.round 返回类型陷阱
1
2
|
long rounded = Math.round(3.7); // 返回 long,不是 int
// int wrong = Math.round(3.7); // 编译报错
|
- 随机数
1
2
3
4
5
6
7
8
|
// [0.0, 1.0)
double r = Math.random();
// [10, 50] 随机整数
int randomInt = 10 + (int)(Math.random() * 41);
// Java 7+ 推荐:ThreadLocalRandom
int better = ThreadLocalRandom.current().nextInt(10, 51);
|
- Math vs StrictMath
1
2
3
4
5
|
// Math:平台优化,速度快,精度略有差异
double mathSin = Math.sin(Math.PI / 2); // 1.0
// StrictMath:严格 IEEE 754,跨平台结果完全一致
double strictSin = StrictMath.sin(Math.PI / 2); // 1.0
|
- BigDecimal 精确计算(金融专用)
1
2
3
4
5
6
7
8
|
BigDecimal price = new BigDecimal("19.99");
BigDecimal quantity = new BigDecimal("3");
// 乘法
BigDecimal total = price.multiply(quantity); // 59.97
// 除法(指定精度 + 舍入模式)
BigDecimal result = total.divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP); // 19.99
|
六、Stream API
6.1 核心定位
链式操作、惰性执行、支持并行,专门用于集合/数组数据处理(过滤、转换、统计、查找),代码极简优雅。三大阶段:创建流 → 中间操作 → 终止操作。
6.2 核心方法
| 概念 |
说明 |
示例 |
| 创建流 |
从集合、数组、IO 等创建 |
list.stream(), Arrays.stream(arr) |
| 中间操作 |
返回新流,惰性执行 |
filter(), map(), sorted() |
| 终止操作 |
触发执行,返回结果 |
collect(), forEach(), reduce() |
| 短路操作 |
提前结束,提高效率 |
findFirst(), anyMatch(), limit() |
6.3. 创建 Stream
下面是创建 Stream 最常用的方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
// 1. 集合创建
List<String> list = Arrays.asList("a","b","c");
Stream<String> stream = list.stream(); // 串行
Stream<String> parallel = list.parallelStream();// 并行
// 2. Map 转流
Map<String, Integer> map = Map.of("a", 1, "b", 2);
Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
// 3. 数组创建
int[] arr = {1,2,3};
IntStream intStream = Arrays.stream(arr);
// 4. 静态方法
Stream<String> stream1 = Stream.of("a","b","c");
Stream<Integer> empty = Stream.empty();
// 5. 基本类型流(推荐,无装箱)
IntStream.of(1,2,3);
LongStream.of(1L,2L);
DoubleStream.of(1.1,2.2);
// 6. 无限流(必须加 limit)
Stream.iterate(0, n -> n+1).limit(10);
Stream.generate(Math::random).limit(5);
// 7. IO 流(必须 try-with-resources 自动关闭)
try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {
lines.forEach(System.out::println);
}
|
6.4. 中间操作
中间操作是惰性的,就是不调用终止操作,中间操作不执行。
- 过滤 / 去重
1
2
3
4
5
6
7
|
List<Integer> list = Arrays.asList(1,2,2,3,4,4);
// filter:条件过滤
list.stream().filter(n -> n%2==0); // 保留偶数
// distinct:去重
list.stream().distinct(); // [1,2,3,4]
|
- 映射(转换数据)
1
2
3
4
5
6
7
8
9
|
// map:一对一转换
list.stream().map(n -> n*2); // 1→2, 2→4...
// flatMap:扁平化(嵌套集合展开)
List<List<Integer>> nested = Arrays.asList(Arrays.asList(1,2), Arrays.asList(3,4));
nested.stream().flatMap(Collection::stream); // [1,2,3,4]
// 基本类型流(性能最优)
list.stream().mapToInt(Integer::intValue);
|
- 排序
1
2
3
4
5
|
// 自然排序
list.stream().sorted();
// 自定义排序
list.stream().sorted(Comparator.reverseOrder()); // 倒序
|
- 截断 / 跳过
1
2
3
4
5
|
list.stream().limit(3); // 取前3个
list.stream().skip(2); // 跳过前2个
// 分页标准写法
stream.skip((page-1)*size).limit(size);
|
- 调试 peek(不影响流,打印日志)
1
2
3
4
|
list.stream()
.filter(n->n>2)
.peek(System.out::println) // 调试
.map(n->n*3);
|
6.5 终止操作
终止操作,执行后流关闭,返回结果。
- 聚合统计
1
2
3
4
5
6
7
|
List<Integer> list = Arrays.asList(1,2,3,4,5);
long count = list.stream().count(); // 5
int sum = list.stream().mapToInt(Integer::intValue).sum(); // 15
int max = list.stream().mapToInt(Integer::intValue).max().orElse(0); //5
int min = list.stream().mapToInt(Integer::intValue).min().orElse(0); //1
double avg = list.stream().mapToInt(Integer::intValue).average().orElse(0); //3.0
|
- 归约 reduce(累加、拼接、乘积)
1
2
3
4
5
|
// 求和
int total = list.stream().reduce(0, Integer::sum);
// 字符串拼接
String str = Stream.of("a","b","c").reduce("", String::concat);
|
- 收集 collect(最常用 → 转 List/Set/Map)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// 转集合
List<Integer> resultList = stream.collect(Collectors.toList());
Set<Integer> resultSet = stream.collect(Collectors.toSet());
// 字符串拼接
String join = stream.map(String::valueOf).collect(Collectors.joining(","));
// 分组
Map<String, List<Integer>> group = list.stream()
.collect(Collectors.groupingBy(n->n%2==0?"偶数":"奇数"));
// 分区(true/false两组),Map<Boolean, List<元素>,key 只有两个:true /false
// {
// false = [1, 2, 3],
// true = [4, 5]
// }
Map<Boolean, List<Integer>> part = list.stream()
.collect(Collectors.partitioningBy(n->n>3));
|
- 查找与匹配(短路操作)
1
2
3
4
5
6
7
8
|
// 查找
Optional<Integer> first = list.stream().findFirst(); // 第一个
Optional<Integer> any = list.stream().findAny(); // 任意一个
// 匹配
boolean anyMatch = list.stream().anyMatch(n->n>3); // 存在一个满足
boolean allMatch = list.stream().allMatch(n->n>0); // 全部满足
boolean noneMatch = list.stream().noneMatch(n->n<0); // 都不满足
|
- 遍历
1
2
|
list.stream().forEach(System.out::println);
list.parallelStream().forEachOrdered(System.out::println); // 保证顺序
|
6.5 性能陷阱与最佳实践
- 流只能使用一次
1
2
3
|
Stream<Integer> s = list.stream();
s.count();
// s.forEach(...); // 报错:流已关闭
|
- 基本类型流比包装流快
1
2
3
4
5
|
// 慢(装箱/拆箱)
list.stream().reduce(0, Integer::sum);
// 快(推荐)
list.stream().mapToInt(Integer::intValue).sum();
|
- 并行流不是万能的
1
2
3
4
5
|
// 小数据量不要用并行
smallList.parallelStream(); // 浪费性能
// 适合:大数据量 + 数组/ArrayList
IntStream.range(0,1000000).parallel().sum();
|
- null 必须手动过滤
1
2
3
|
list.stream()
.filter(Objects::nonNull) // 必须加
.forEach(...);
|
- 完整标准示例(最常用模板)
1
2
3
4
5
6
7
|
// 需求:过滤偶数 → 平方 → 排序 → 转集合
List<Integer> result = list.stream()
.filter(Objects::nonNull)
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.sorted()
.collect(Collectors.toList());
|
七、StringUtils(Apache Commons Lang)
7.1 核心定位
Java 原生 String 增强工具类,最大优势:空安全(传 null 不抛 NPE),日常开发字符串处理首选。必须依赖:commons-lang3
7.2 与原生 Java 对比
| 操作 |
原生 Java |
StringUtils |
优势 |
| 判空 |
str == null || str.isEmpty() |
StringUtils.isEmpty(str) |
简洁、可读 |
| 判空白 |
str == null || str.trim().isEmpty() |
StringUtils.isBlank(str) |
包含全空白判断 |
| 默认值 |
str != null ? str : default |
StringUtils.defaultString(str) |
简洁 |
| 截取 |
手动检查边界 |
StringUtils.substring(str, 0, 5) |
安全(不抛异常) |
| 分割 |
split()(正则) |
split()(按字符) |
性能更好 |
7.3 代码示例
- 最核心:空值判断(最常用)
1
2
3
4
5
6
7
8
9
|
// isEmpty:null 或 "" → true;" " → false
StringUtils.isEmpty(null); // true
StringUtils.isEmpty(""); // true
StringUtils.isEmpty(" "); // false
// isBlank:null / "" / 全是空格 → 都返回 true(工作最常用)
StringUtils.isBlank(null); // true
StringUtils.isBlank(""); // true
StringUtils.isBlank(" "); // true
|
- 空值默认值(防止 null)
1
2
3
4
5
6
|
// null 转 ""
StringUtils.defaultString(null); // ""
// null 或空白 → 用默认值
StringUtils.defaultIfBlank(null, "未知"); // "未知"
StringUtils.defaultIfBlank(" ", "默认"); // "默认"
|
- 安全截取(不会越界异常)
1
2
3
4
5
6
7
8
|
String str = "Hello";
// 超出长度不会报错,自动截断
StringUtils.substring(str, 0, 100); // "Hello"
StringUtils.substring(str, 2); // "llo"
// 取两个字符串中间的内容
StringUtils.substringBetween("[123]", "[", "]"); // "123"
|
- 字符串分割(非正则,性能高)
1
2
3
4
5
6
7
|
String str = "a,b,c,d";
// 按字符分割(不是正则,比原生 String.split 快)
StringUtils.split(str, ","); // [a, b, c, d]
// 限制分割次数
StringUtils.split("a,b,c,d", ",", 2); // [a, b,c,d]
|
- 字符串连接(数组/集合拼接)
1
2
3
4
5
|
List<String> list = Arrays.asList("a","b","c");
// 用分隔符连接
StringUtils.join(list, ", "); // "a, b, c"
StringUtils.join(new String[]{"a","b"}, "-"); // "a-b"
|
- 去空格 / 删除内容
1
2
3
4
5
6
7
8
9
10
|
String str = " Hello World ";
// 去首尾空格
StringUtils.trim(str); // "Hello World"
// 删除所有空格
StringUtils.deleteWhitespace(str); // "HelloWorld"
// 删除指定子串
StringUtils.remove("Hello", "e"); // "Hllo"
|
- 包含判断(空安全)
1
2
3
4
5
6
7
8
9
|
// 包含子串(传 null 也不报错)
StringUtils.contains("Hello", "H"); // true
StringUtils.contains(null, "a"); // false
// 忽略大小写
StringUtils.containsIgnoreCase("Hello", "h"); // true
// 统计子串出现次数
StringUtils.countMatches("ababa", "a"); // 3
|
- 填充、补齐、重复
1
2
3
4
5
6
7
8
|
// 左侧补 0(常用:编号、序号)
StringUtils.leftPad("5", 3, "0"); // "005"
// 右侧补齐
StringUtils.rightPad("Hi", 5, "!"); // "Hi!!!"
// 重复字符串
StringUtils.repeat("ab", 3); // "ababab"
|
- 大小写转换
1
2
3
|
StringUtils.upperCase("hello"); // "HELLO"
StringUtils.lowerCase("HELLO"); // "hello"
StringUtils.capitalize("hello"); // "Hello"(首字母大写)
|
- Maven 依赖(必须加)
1
2
3
4
5
|
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
|
八、工具类选择速查表
| 场景 |
推荐工具类 |
说明 |
| 数组排序/查找 |
Arrays |
双轴快排、二分查找 |
| 集合排序/同步 |
Collections |
线程安全包装、不可变视图 |
| 空值处理 |
Objects |
空安全比较、参数校验 |
| 文件读写 |
Files/Paths |
NIO.2,支持 Stream |
| 数学计算 |
Math / BigDecimal |
基本运算 / 精确计算 |
| 数据处理 |
Stream API |
函数式、链式操作 |
| 字符串处理 |
StringUtils |
Apache Commons,更强大 |
九、总结
核心要点
- Arrays:掌握
asList() 的陷阱,优先使用 copyOf() 进行数组扩容
- Collections:理解同步包装与并发集合的区别,善用不可变集合
- Objects:防御式编程的基础,所有参数校验都应使用
requireNonNull()
- Files/Paths:NIO.2 是现代 Java 文件操作的标准,务必掌握
try-with-resources
- Math:金融计算必须使用
BigDecimal,注意浮点数精度问题
- Stream:理解惰性求值和短路操作,避免并行流误用
- StringUtils:项目中引入 Apache Commons Lang 可大幅提升字符串处理效率
性能建议
- 小数据量:直接使用循环,避免 Stream overhead
- 大数据量:使用 Stream 并行处理,但确保数据源可高效拆分
- 频繁操作:优先使用基本类型流(
IntStream 等)避免装箱
- 字符串处理:大量操作时,Apache Commons Lang 性能优于原生方法