Efficiently joining text in nested lists

ykaganovich :

Suppose I have a text represented as a collection of lines of words. I want to join words in a line with a space, and join lines with a newline:

class Word {
  String value;
}

public static String toString(List <List <Word>> lines) {
    return lines.stream().map(
            l -> l.stream().map(w -> w.value).collect(Collectors.joining(" "))
    ).collect(Collectors.joining("\n"));
}

This works fine, but I end up creating an intermediate String object for each line. Is there a nice concise way of doing the same without the overhead?

Holger :

You can use

public static String toString(List<List<Word>> lines) {
    return lines.stream()
        .map(l -> l.stream()
                   .map(w -> w.value)
                   .collect(() -> new StringJoiner(" "),
                            StringJoiner::add,
                            StringJoiner::merge))
        .collect(() -> new StringJoiner("\n"),
                       StringJoiner::merge,
                       StringJoiner::merge).toString();
}

The inner collect basically does what Collectors.joining(" ") does, but omits the final StringJoiner.toString() step.

Then, the outer collect differs from an ordinary Collectors.joining("\n") in that it accepts StringJoiner as an input and combines them using merge. This relies on a documented behavior:

If the other StringJoiner is using a different delimiter, then elements from the other StringJoiner are concatenated with that delimiter and the result is appended to this StringJoiner as a single element.

This is done internally on the StringBuilder/character data level without creating a String instance while retaining the intended semantic.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=476699&siteId=1
Recommended