1. 概述

在处理字符串集合时,使用特定分隔符将这些字符串连接起来是一项常见的任务。幸运的是,我们有许多解决方案可供选择,包括使用String.join()方法和Collectors.joining()方法。

在这篇快速教程中,我们将探索一个有趣的字符串连接问题:以更自然语言的方式连接字符串。

2. 问题介绍

让我们通过一个例子来理解这个问题。假设我们有一个字符串列表{“A”, “B”, “C”, “D”}。如果我们想用逗号作为分隔符,结果将是“A, B, C, D”。到目前为止,一切正常。

然而,如果我们要得到遵循英语语法的连接结果,期望的结果应该是“A, B, C and D”“A, B, C, and D”。稍后我们会解释为什么会有两种形式。但至少我们知道,这并不是直接通过String.join()Collectors.joining()方法调用能得到的结果。

在上述示例中,“C”“and”之间的逗号被称为牛津逗号或哈佛逗号。关于哪种风格更为精确,存在一些讨论。但这不是我们的重点。我们的目标是创建一个方法来支持这两种情况。

因此,给定一个包含超过两个字符串元素的列表,例如{“A”, “B”, “C”, …, “X”, “Y”},根据需求,我们可能得到两种结果:

  • 使用牛津逗号 - “A, B, C, …, X and Y”
  • 不使用牛津逗号 - “A, B, C, …, X, and Y”

此外,我们只讨论了至少包含三个元素的列表。如果列表包含少于三个元素,结果可能会有所不同:

  • 对于空列表,返回空字符串,所以{ }变为“”
  • 对于只有一个元素的列表,返回该元素。例如,{“A”}变为“A”
  • 当处理包含两个字符串元素的列表时,不使用逗号将它们组合在一起。例如,{“A”, “B”}变成“A and B”

接下来,我们将创建一个方法,以自然语言的方式连接字符串列表。为了简化,我们假设输入列表不为null,并且不包含null或空字符串元素。实际上,如果列表包含空或null字符串,我们可以在实际操作前先进行过滤。

3. 创建joinItemsAsNaturalLanguage()方法

首先,让我们看看方法实现,然后理解它是如何工作的:

String joinItemsAsNaturalLanguage(List<String> list, boolean oxfordComma) {
    if (list.size() < 3) {
        return String.join(" and ", list);
    }
    // list has at least three elements
    int lastIdx = list.size() - 1;

    StringBuilder sb = new StringBuilder();
    return sb.append(String.join(", ", list.subList(0, lastIdx)))
      .append(oxfordComma ? ", and " : " and ")
      .append(list.get(lastIdx))
      .toString();
}

现在,让我们快速浏览代码。首先,我们处理列表包含不到三个元素的情况,使用String.join(“ and “, list)

然后,如果列表包含三个或更多的字符串,我们使用“, “作为分隔符连接输入子列表(排除最后一个字符串)中的元素。最后,我们将连接后的结果与最后一个元素加上“and”。当然,也要考虑牛津逗号选项。

值得注意的是,我们不应该先用逗号将所有元素连接起来,然后再替换最后一个逗号为“and”。这是因为最后一个元素也可能包含逗号。

让我们不使用牛津逗号测试我们的解决方案:

assertEquals("", joinItemsAsNaturalLanguage(emptyList(), false));
assertEquals("A", joinItemsAsNaturalLanguage(List.of("A"), false));
assertEquals("A and B", joinItemsAsNaturalLanguage(List.of("A", "B"), false));
assertEquals("A, B, C, D and I have a comma (,)", joinItemsAsNaturalLanguage(List.of("A", "B", "C", "D", "I have a comma (,)"), false));

最后,使用牛津逗号测试:

assertEquals("", joinItemsAsNaturalLanguage(emptyList(), true));
assertEquals("A", joinItemsAsNaturalLanguage(List.of("A"), true));
assertEquals("A and B", joinItemsAsNaturalLanguage(List.of("A", "B"), true));
assertEquals("A, B, C, D, and I have a comma (,)", joinItemsAsNaturalLanguage(List.of("A", "B", "C", "D", "I have a comma (,)"), true));

4. 总结

在这篇文章中,我们讨论了如何以自然语言的方式连接字符串列表的问题。我们也学习了如何创建一个解决此问题的方法。如往常一样,每个示例的完整源代码可在GitHub上找到。