Friends of OpenJDK Today

Teeing: Hidden Gem in the Java API

May 12, 2021

Author(s)

  • Avatar photo
    Nicolas Frankel

    Nicolas is a developer advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). ... Learn more

Last week, I described a use-case for a custom Stream Collector. I received a intriguing comment on Twitter:

Hats off to you, Miguel! Your comment revealed a method I didn't know!

So I decided to investigate what is the teeing() method about.

Returns a Collector that is a composite of two downstream collectors. Every element passed to the resulting collector is processed by both downstream collectors, then their results are merged using the specified merge function into the final result.

The resulting collector functions do the following:

  • supplier: creates a result container that contains result containers obtained by calling each collector's supplier
  • accumulator: calls each collector's accumulator with its result container and the input element
  • combiner: calls each collector's combiner with two result containers
  • finisher: calls each collector's finisher with its result container, then calls the supplied merger and returns its result.

-- JavaDocs

We can indeed replace our custom Collector with two simple Collector implementations, one aggregating price rows and the other summing the cart's price.

Let's look at the final code and explain it line by line.

public PriceAndRows getPriceAndRows(Cart cart) {
  return cart.getProducts()
      .entrySet()
      .stream()
      .map(CartRow::new)                           // 1
      .collect(Collectors.teeing(                  // 2
          Collectors.reducing(                     // 3
              BigDecimal.ZERO,                     // 3.1
              CartRow::getRowPrice,                // 3.2
              BigDecimal::add),                    // 3.3
          Collectors.toList(),                     // 4
          PriceAndRows::new                        // 5
      ));
}
  1. Map each Entry to a CartRow
  2. Call the teeing() method
  3. The first collector computes the price. It's a simple reducing() call, with:
    1. The starting element
    2. A function to extract a Price from a CartRow
    3. A BinaryOperator to add two prices together
  4. The second collector aggregates the CartRow into a list
  5. Finally, the last parameter creates a new object that aggregates the results from the first and the second collector

On the implementation side, teeing():

  1. Extracts each of the individual components of both Collector, i.e., supplier(), accumulator(), combiner() and finisher()`
  2. Pairs them side-by-side
  3. Creates a single new Collector by passing the pairs

Thus, there will be a single Collector and a single pass in the end.

I hope this post made you consider using teeing() before creating a custom Collector. Thanks again to Miguel!

By the way, I'm always happy to learn new things. In case you've got insights to share, you can use the commenting system below or Twitter.

The complete source code for this post can be found on Github in Maven format.

To go further:

Originally published at A Java Geek on May 9th, 2021

Topics:

Author(s)

  • Avatar photo
    Nicolas Frankel

    Nicolas is a developer advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). ... Learn more

Comments (0)

Your email address will not be published. Required fields are marked *

Highlight your code snippets using [code lang="language name"] shortcode. Just insert your code between opening and closing tag: [code lang="java"] code [/code]. Or specify another language.

Save my name, email, and website in this browser for the next time I comment.

Subscribe to foojay updates:

https://foojay.io/feed/
Copied to the clipboard