1. 概述

在这个教程中,我们将探讨几种从String中获取最后 n 个字符的方法。假设我们有一个表示日期的String

String s = "10-03-2024";

我们要从中提取年份,换句话说,我们只想要最后四个字符,所以 n 是:

int n = 4;

2. 使用 substring() 方法

我们可以使用 substring() 方法的一个重载版本,它从 beginIndex 开始(包括)并到 endIndex 结束(不包括):

@Test
void givenString_whenUsingTwoArgSubstringMethod_thenObtainLastNCharacters() {
    int beginIndex = s.length() - n;
    String result = s.substring(beginIndex, s.length());

    assertThat(result).isEqualTo("2024");
}

由于我们想要最后 n 个字符,我们首先通过从 String 的长度中减去 n 来确定 beginIndex。最后,我们将 endIndex 设置为 String 的长度。

而且,因为我们只对最后 n 个字符感兴趣,我们可以利用更方便的 substring() 方法重载版本,它只需要一个 beginIndex 参数:

@Test
void givenString_whenUsingOneArgSubstringMethod_thenObtainLastNCharacters() {
    int beginIndex = s.length() - n;

    assertThat(s.substring(beginIndex)).isEqualTo("2024");
}

这个方法返回从 beginIndex 包含到最后一个字符的所有字符。

最后值得一提的是,String 类的 subSequence() 方法也存在,它在底层使用 substring()。尽管它可以解决我们的场景,但从可读性角度来看,直接使用 substring() 方法更为合适。

3. 使用 Apache Commons Lang 3 的 right() 方法

要使用 Apache Commons Lang 3,我们需要将其依赖添加到 pom.xml 中:

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

我们可以使用 StringUtils.right() 方法来获取最后 n 个字符:

@Test
void givenString_whenUsingStringUtilsRight_thenObtainLastNCharacters() {
    assertThat(StringUtils.right(s, n)).isEqualTo("2024");
}

这里,我们只需提供要处理的 String 和我们想要从 String 末尾返回的字符数。因此,对于我们的用例,这是首选解决方案,因为它消除了计算索引的需求,从而产生更易于维护且无bug的代码。

4. 使用流API

现在,让我们看看函数式编程解决方案可能是什么样子。

我们的流源(Java 8 流简介中的一个选项)可以是使用 chars() 方法。此方法返回一个 IntStream,其中的原始 int 元素代表 String 中的字符:

@Test
void givenString_whenUsingIntStreamAsStreamSource_thenObtainLastNCharacters() {
    String result = s.chars()
      .mapToObj(c -> (char) c)
      .skip(s.length() - n)
      .map(String::valueOf)
      .collect(Collectors.joining());

    assertThat(result).isEqualTo("2024");
}

这里,我们使用 mapToObj() 中间操作将每个 int 元素转换为 Character 对象。现在我们有了一个 Character 流,我们可以使用 skip() 操作保留特定索引之后的元素。

接下来,我们使用 map() 操作和 String::valueOf 方法引用。我们将每个 Character 元素转换为 String,因为我们想使用终端操作 collect()Collectors.joining() 静态方法。

另一种函数式方法是最初使用 toCharArray() 方法:

@Test
void givenString_whenUsingStreamOfCharactersAsSource_thenObtainLastNCharacters() {
    String result = Arrays.stream(ArrayUtils.toObject(s.toCharArray()))
      .skip(s.length() - n)
      .map(String::valueOf)
      .collect(Collectors.joining());

    assertThat(result).isEqualTo("2024");
}

此方法返回一个 char 原始数组。我们可以使用 Apache Commons Lang 3 的 ArrayUtils.toObject() 将数组转换为 Character 元素的数组。然后,我们使用静态方法 Arrays.stream() 获取一个 Character 流。从这里开始,我们的逻辑保持不变,以获取最后 n 个字符。

如上所述,我们看到使用函数式编程方法实现目标需要做很多工作。这表明只有在适当的时候才应使用函数式编程。

5. 总结

在这篇文章中,我们探讨了从 String 中获取最后 n 个字符的不同方法。我们强调了命令式方法比函数式方法提供了更简洁和易读的解决方案。需要注意的是,本文中提取年份的 String 示例只是为了演示目的。有更适合的实现方式,比如将 String 解析为 LocalDate 并使用 getYear() 方法。

如往常一样,本文中的代码示例可在 GitHub 上找到。