Perform multiple unrelated operations on elements of a single stream in Java

MC Emperor :

How can I perform multiple unrelated operations on elements of a single stream?

Say I have a List<String> composed from a text. Each string in the list may or may not contain a certain word, which represents an action to perform. Let's say that:

  • if the string contains 'of', all the words in that string must be counted
  • if the string contains 'for', the portion after the first occurrence of 'for' must be returned, yielding a List<String> with all substrings

Of course, I could do something like this:

List<String> strs = ...;

List<Integer> wordsInStr = strs.stream()
    .filter(t -> t.contains("of"))
    .map(t -> t.split(" ").length)
    .collect(Collectors.toList());

List<String> linePortionAfterFor = strs.stream()
    .filter(t -> t.contains("for"))
    .map(t -> t.substring(t.indexOf("for")))
    .collect(Collectors.toList());

but then the list would be traversed twice, which could result in a performance penalty if strs contained lots of elements.

Is it possible to somehow execute those two operations without traversing twice over the list?

Flown :

If you want a single pass Stream then you have to use a custom Collector (parallelization possible).

class Splitter {
  public List<String> words = new ArrayList<>();
  public List<Integer> counts = new ArrayList<>();

  public void accept(String s) {
    if(s.contains("of")) {
      counts.add(s.split(" ").length);
    } else if(s.contains("for")) {
      words.add(s.substring(s.indexOf("for")));
    }
  }

  public Splitter merge(Splitter other) {
    words.addAll(other.words);
    counts.addAll(other.counts);
    return this;
  }
}
Splitter collect = strs.stream().collect(
  Collector.of(Splitter::new, Splitter::accept, Splitter::merge)
);
System.out.println(collect.counts);
System.out.println(collect.words);

Guess you like

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