【问题标题】:How do I group lines of strings separated by empty lines using java stream如何使用java流对由空行分隔的字符串行进行分组
【发布时间】:2020-07-01 14:30:43
【问题描述】:

我有以下输入,我希望使用空行作为逻辑分隔符对它们进行分组。 输入数据类型: List<String>

line1
line2
line3

line4
line5
line6

line7
line8

预期输出: 类型:列表

{{line1, line2, line3}, {line4, line5, line6}, {line7, line8}}

虽然使用命令式编程很简单,但我正在寻找一种在 Java 中使用函数式编程的解决方案。

【问题讨论】:

  • 您的输入是单个字符串还是字符串列表或文本文件或其他内容?
  • 输入是一个列表

标签: java functional-programming


【解决方案1】:

你没有说你使用的是什么 Java 版本,但这应该在 Java8+ 中工作。 reduce 上的 stream 函数可以为您提供所需的内容,如果您使用具有 3 个 args 的函数作为初始值,在这种情况下是一个包​​含字符串列表的列表、一个接受当前结果和单个字符串的 lambda 函数。当它看到一个空行时,这确实完成了添加新列表的所有工作。最后,一个组合器解释了如何组合 2 个列表,但实际上只是为了帮助编译器了解正在发生的事情。在这种情况下,它什么也不做,所以它总是可以返回一个空列表。

String input = "line1\n" +
        "line2\n" +
        "line3\n" +
        "\n" +
        "line4\n" +
        "line5\n" +
        "line6\n" +
        "\n" +
        "line7\n" +
        "line8";
List<String> strings = Arrays.stream(input.split("\n")).collect(Collectors.toList());

List<List<String>> initial = new ArrayList<>();
initial.add(new ArrayList<>());
List<List<String>> result = strings.stream().reduce(initial, (subtotal, element) -> {
    if (element.trim().isEmpty()) {
        subtotal.add(new ArrayList<>());
    } else {
        subtotal.get(subtotal.size() - 1).add(element);
    }
    return subtotal;

}, (list1, list2) -> emptyList());

我得到List&lt;List&lt;String&gt;&gt; 的结果[[line1, line2, line3], [line4, line5, line6], [line7, line8]] 的输出,我相信这就是您所追求的。

【讨论】:

  • 抱歉,好像没用。你有打印出来的吗?
  • @Mox 你需要改变什么?我可以为其他人更新答案。
  • 我已经编辑了你的答案。不太确定你能不能看到它
【解决方案2】:

可以通过已知的空字符串作为分隔符将列表拆分为子列表:

List<String> data = Arrays.asList("a", "b", "c", "", "d", "", "e", "f");

int[] indexes = 
                Stream.of(
                  IntStream.of(-1), 
                  IntStream.range(0, data.size()).filter(i -> data.get(i).isEmpty()), 
                  IntStream.of(data.size())
                ) 
                .flatMapToInt(ix -> ix)
                .toArray();
List<List<String>> subSets = 
          IntStream.range(0, indexes.length - 1)
                   .mapToObj(i -> data.subList(indexes[i] + 1, indexes[i + 1]))
                   .collect(Collectors.toList());
subSets.forEach(System.out::println);

输出是:

[a, b, c]
[d]
[e, f]

或者可以通过重新加入和重新拆分输入数据来实现(如果输入为List&lt;String&gt;):

List<String> data = Arrays.asList("a", "b", "c", "", "d", "", "e", "f");

Arrays.stream(
    data.stream()
    .collect(Collectors.joining("\n")).split("\n{2}") // split by double 'empty lines'
)
.map(s -> Arrays.stream(s.split("\n")).collect(Collectors.toList()))
.collect(Collectors.toList())
.forEach(System.out::println);

输出同上。

【讨论】:

  • 您的第二种方法容易出错。假设那些## 或#### 不会出现在行中。
  • 是的,这样的假设是根据测试数据做出的。不过,我在更新中将这些自定义分隔符替换为 \n。
猜你喜欢
  • 1970-01-01
  • 2012-12-25
  • 1970-01-01
  • 2013-04-21
  • 2014-09-17
  • 2014-03-02
  • 2017-03-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多