简体   繁体   中英

How do I group lines of strings separated by empty lines using java stream

I have the following input and I would like them to be grouped using the empty line as the logical divider. input datatype: List<String>

line1
line2
line3

line4
line5
line6

line7
line8

expected output: type: List<List>

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

While this is trivial using imperative programming, I am looking for a solution that uses functional programming in Java.

You don't say what Java version you are using, but this should work in Java8+. The reduce function on stream can give you what you want if you use the one with 3 args an initial value in this case a list with lists of string, a lambda function that takes the current result and a single string. This really does all the work of adding a new list when it sees a blank line. Finally, a combiner that explains how to combine 2 lists but is really just to help the compiler understand what is going on. In this case it does nothing but that so it can always return an empty list.

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());

I get the output of a List<List<String>> of result[[line1, line2, line3], [line4, line5, line6], [line7, line8]] which I believe is what you are after.

It can be done by splitting the list into sublist by the known empty string as a separator:

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);

The output is:

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

Or it can be implemented via re-joining and re-splitting the input data (if the input is List<String> ):

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);

The output is the same as above.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM