What is the (kind of) inverse operation to Java's Stream.flatMap()?

前端 未结 5 912
别那么骄傲
别那么骄傲 2021-02-19 05:40

The Stream.flatMap() operation transforms a stream of

a, b, c

into a stream that contains zero or more elements for each input el

5条回答
  •  时光取名叫无心
    2021-02-19 06:24

    Finally I figured out that flatMap is its own "inverse" so to say. I oversaw that flatMap not necessarily increases the number of elements. It may also decrease the number of elements by emitting an empty stream for some of the elements. To implement a group-by operation, the function called by flatMap needs minimal internal state, namely the most recent element. It either returns an empty stream or, at the end of a group, it returns the reduced-to group representative.

    Here is a quick implementation where groupBorder must return true if the two elements passed in do not belong to the same group, i.e. between them is the group border. The combiner is the group function that combines, for example (1,a), (1,a), (1,a) into (3,a), given that your group elements are, tuples (int, string).

    public class GroupBy implements Function>{
    
      private final BiPredicate groupBorder;
      private final BinaryOperator combiner;
      private X latest = null;
    
      public GroupBy(BiPredicate  groupBorder,
                     BinaryOperator combiner) {
        this.groupBorder = groupBorder;
        this.combiner = combiner;
      }
    
      @Override
      public Stream apply(X elem) {
        // TODO: add test on end marker as additonal parameter for constructor
        if (elem==null) {
          return latest==null ? Stream.empty() : Stream.of(latest);
        }
        if (latest==null) {
          latest = elem;
          return Stream.empty();
        }
        if (groupBorder.test(latest, elem)) {
          Stream result = Stream.of(latest);
          latest = elem;
          return result;
        }
        latest = combiner.apply(latest,  elem);
        return Stream.empty();
      }
    }
    

    There is one caveat though: to ship the last group of the whole stream, an end marker must be stuck as the last element into the stream. The above code assumes it is null, but an additional end-marker-tester could be added.

    I could not come up with a solution that does not rely on the end marker.

    Further I did not also convert between incoming and outgoing elements. For a unique-operation, this would just work. For a count-operation, a previous step would have to map individual elements to a counting object.

提交回复
热议问题