1. 概述

Java 中的 String.trim() 方法可以同时去除字符串首尾的空白字符,但标准库并没有提供仅去除左侧(L-Trim)或右侧(R-Trim)空白的原生方法。

本文将介绍几种实现 L-Trim 和 R-Trim 的方式,并通过性能对比帮你选出最适合的方案。✅

⚠️ 注意:本文讨论的是“仅去左”或“仅去右”,不是 trim() 那种双向去除。


2. 使用 while 循环(最直接)

最简单粗暴的方式就是手动遍历字符,找到第一个非空白位。

L-Trim 实现

从左往右扫描,直到遇到非空白字符为止:

int i = 0;
while (i < s.length() && Character.isWhitespace(s.charAt(i))) {
    i++;
}
String ltrim = s.substring(i);

R-Trim 实现

从右往左扫描,跳过尾部空白:

int i = s.length() - 1;
while (i >= 0 && Character.isWhitespace(s.charAt(i))) {
    i--;
}
String rtrim = s.substring(0, i + 1);

✅ 优点:逻辑清晰、无依赖、性能优秀
❌ 缺点:代码略冗长,需手动封装成工具方法


3. 使用 String.replaceAll + 正则表达式

利用正则表达式也能一行搞定:

String ltrim = src.replaceAll("^\\s+", "");
String rtrim = src.replaceAll("\\s+$", "");
  • ^\\s+:匹配行首的一个或多个空白字符
  • \\s+$:匹配行尾的一个或多个空白字符

⚠️ 注意:这里的 \\s 不仅包括空格,还包括 \t, \n, \r, \f 等 Unicode 空白字符,与 Character.isWhitespace() 行为一致。

✅ 优点:代码简洁,适合临时脚本
❌ 缺点:每次调用都会编译正则,频繁使用时性能较差


4. 使用 Pattern.compile + matcher(复用正则)

如果需要高频调用,建议将正则表达式预编译,避免重复解析:

private static final Pattern LTRIM = Pattern.compile("^\\s+");
private static final Pattern RTRIM = Pattern.compile("\\s+$");

String ltrim = LTRIM.matcher(s).replaceAll("");
String rtrim = RTRIM.matcher(s).replaceAll("");

✅ 优点:正则只编译一次,性能优于 String.replaceAll
❌ 缺点:仍不如纯字符遍历快,且占用静态内存


5. 使用 Apache Commons Lang

Apache Commons 是 Java 生态中最常用的工具库之一,其 StringUtils 提供了专门的接口:

添加依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.14.0</version>
</dependency>

使用 stripStart 和 stripEnd

String ltrim = StringUtils.stripStart(src, null);
String rtrim = StringUtils.stripEnd(src, null);

null 表示按默认空白字符处理(等价于 Character.isWhitespace

✅ 优点:

  • API 设计清晰
  • 经过大量生产验证
  • 支持自定义字符集(第二个参数可指定要去除的字符)

⚠️ 建议:项目中已有 Commons 依赖时,首选方案。


6. 使用 Google Guava

Guava 作为另一个主流工具库,提供了更语义化的 API:

添加依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.0.1-jre</version>
</dependency>

使用 CharMatcher

String ltrim = CharMatcher.whitespace().trimLeadingFrom(s);
String rtrim = CharMatcher.whitespace().trimTrailingFrom(s);

✅ 优点:

  • API 极其清晰,“trimLeadingFrom”一看就懂
  • CharMatcher 功能强大,可扩展性好(比如自定义匹配规则)

❌ 缺点:相比 Commons,Guava 更重,如果不是 already in use,不建议仅为 trim 引入


7. 性能对比(JMH 基准测试)

我们使用 JMH 对上述方案进行性能压测,测试字符串为:

src = "       White spaces left and right          ";

7.1 测试配置

@Fork(5)
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)

每个 benchmark 执行 L-Trim + R-Trim 并校验结果。

7.2 各方案性能数据(单位:ns/op)

方案 平均耗时 (ns) 排名
while 循环 110.379 🥇
Apache Commons 108.718 🥇
Guava 113.601 🥇
Pattern.matcher 850.085 🟨
String.replaceAll 1046.660 🟥

7.3 结果分析

  • 前三甲几乎打平while、Commons、Guava 性能非常接近,差异在误差范围内
  • ⚠️ 正则方案慢一个数量级:尤其是 String.replaceAll,每次都重新编译正则,代价高
  • 🔁 Pattern.compile 复用后性能提升明显,但仍远不如前三种

💡 结论:纯字符遍历和主流工具库性能最佳,正则适合低频场景。


8. 总结与建议

方案 推荐场景
while 循环 无外部依赖、追求极致性能、可封装成工具类
✅ Apache Commons 项目已引入 commons-lang3,首选方案
✅ Guava 已使用 Guava 生态,API 更优雅
⚠️ Pattern.compile 高频使用正则且需复用时
String.replaceAll 仅用于一次性脚本或低频调用

最终代码示例已托管至 GitHub:

👉 https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-string-operations-2

踩坑提醒:别再用 replaceAll 做 trim 操作了,性能差还容易被误用!


原始标题:L-Trim and R-Trim Alternatives in Java