21

Take a look at this piece of code.

// group by price, uses 'mapping' to convert List<Item> to Set<String>
    Map<BigDecimal, Set<String>> result =
            items.stream().collect(
                    Collectors.groupingBy(Item::getPrice,
                            Collectors.mapping(Item::getName, Collectors.toSet())
                    )
            );

Is groupingBy and Mapping interchangeable? What is their differences?

For the third parameter in collect(), would I get the same output type Map if I used Collectors.toList() instead of Collectors.toSet()? I heard that toList() is a more popular option.

Pshemo
  • 122,468
  • 25
  • 185
  • 269
NoMoreErrors
  • 1,203
  • 2
  • 13
  • 14
  • 2
    `toList()` is “more popular” in that it is the default when no additional collector has been specified, i.e. `Collectors.groupingBy(Item::getPrice)` has the same outcome has `Collectors.groupingBy(Item::getPrice, Collectors.toList())`. – Holger Nov 28 '16 at 10:11
  • See https://stackoverflow.com/q/45231351/32453 – rogerdpack May 07 '21 at 19:10

2 Answers2

24

No, the two are completely different.

Collectors.groupingBy takes a function which creates keys and returns a collector which returns a map from keys to collections of objects in the stream which have that same key.

Collectors.mapping, on the other hand, takes a function and another collector, and creates a new collector which first applies the function and then collects the mapped elements using the given collectors. Thus, the following are equivalent:

items.stream().map(f).collect(c);
items.stream().collect(Collectors.mapping(f, c));

Collectors.mapping is most useful in situations where you do not have a stream, but you need to pass a collector directly. An example of such a situation is when using Collectors.groupingBy.

items.stream().collect(Collectors.groupingBy(Item::getPrice, Collectors.toSet()))

yields a Map<BigDecimal,Set<Item>> (assuming getPrice() returns a BigDecimal). However,

items.stream().collect(Collectors.groupingBy(Item::getPrice,
    Collectors.mapping(Item::getName, Collectors.toSet())))

returns a Map<BigDecimal,Set<String>>. Before collecting the items, it first applies Item.getName to them.

Dheeru Mundluru
  • 373
  • 3
  • 10
Hoopje
  • 12,677
  • 8
  • 34
  • 50
20

Is groupingBy and Mapping interchangeable?

No, they are completely different. groupingBy lets you create a Map where the key is the first argument passed to groupingBy and the value is a List of the element type of the Stream.

Collectors.groupingBy(Item::getPrice) would generate a Map<BigDecimal, List<Item>> (assuming Item::getPrice returns a BigDecimal. Passing the mapping Collector as an argument to Collectors.groupingBy() allows you to change the value of the output map (in your example, you change it to Set<String>).

For the third parameter in collect(), would I get the same output type Map if I used Collectors.toList() instead of Collectors.toSet()?

No, you would get a Map<BigDecimal, List<String>>.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • 1
    I couldn't format this sorry!! If grouping by creates a map, then whats the difference between groupingby and Collectors.toMap() or Collectors.Map()? Map mapAliasAddress = Files.lines(Paths.get(fileName)) .map(s -> s.split(",")) .map(sArr -> new EmailAddress(sArr[1], sArr[0])) .collect(Collectors.toMap( email -> email.getAlias(), email -> email )); – NoMoreErrors Nov 27 '16 at 07:29
  • 2
    @NoMoreErrors `Collectors.toMap` creates a `Map` without grouping, i.e. each element of the input Stream is mapped to an Entry of the output Map (assuming there are no duplicate keys). `groupingBy` maps multiple elements of the input Stream that share some property into a single Entry of the output Map. – Eran Nov 27 '16 at 07:36
  • So am I right to say that Collectors.toMap and Collectors.Mapping is exactly the same? – NoMoreErrors Nov 27 '16 at 08:54
  • I'm referring to toMap – NoMoreErrors Nov 27 '16 at 10:18
  • 2
    @NoMoreErrors No, `Collectors.mapping()` cannot produce a `Map` (unless it is used in combination with `groupingBy`). For example, if you have a `List`, you can produce a `Set` with `Set lengths = list.stream ().collect(Collectors.mapping (s->s.length (),Collectors.toSet()));`, but not a `Map`. – Eran Nov 27 '16 at 10:30