Java Reference
In-Depth Information
already in the core library and is called
counting
. So, we can rewrite the example into
Example 5-14. Using collectors to count the number of albums for each artist
public
public
Map
<
Artist
,
Long
>
numberOfAlbums
(
Stream
<
Album
>
albums
) {
return
return
albums
.
collect
(
groupingBy
(
album
->
album
.
getMainMusician
(),
counting
()));
}
This form of
groupingBy
divides elements into
buckets
. Each bucket gets associated with
the key provided by the classifier function:
getMainMusician
. The
groupingBy
operation
then uses the downstream collector to collect each bucket and makes a map of the results.
Let's consider another example, in which instead of building up a grouping of albums, we
just want their names. Again, one approach is to take our original collector and then fix up
Example 5-15. A naive approach to finding the names of every album that an artist has pro-
duced
public
public
Map
<
Artist
,
List
<
String
>>
nameOfAlbumsDumb
(
Stream
<
Album
>
albums
) {
Map
<
Artist
,
List
<
Album
>>
albumsByArtist
=
albums
.
collect
(
groupingBy
(
album
->
album
.
getMainMusician
()));
Map
<
Artist
,
List
<
String
>>
nameOfAlbums
=
new
new
HashMap
<>();
for
for
(
Entry
<
Artist
,
List
<
Album
>>
entry
:
albumsByArtist
.
entrySet
()) {
nameOfAlbums
.
put
(
entry
.
getKey
(),
entry
.
getValue
()
.
stream
()
.
map
(
Album:
:
getName
)
.
collect
(
toList
()));
}
return
return
nameOfAlbums
;
}
Again, we can produce nicer, faster, and easier-to-parallelize code using another collector.
We already know that we can group our albums by the main artist using the
groupingBy
col-
lector, but that would output a
Map<Artist, List<Album>>
. Instead of associating a list of
albums with each
Artist
, we want to associate a list of strings, each of which is the name of
an album.
In this case, what we're really trying to do is perform a
map
operation on the list from the
Artist
to the album name. We can't just use the
map
method on streams because this list is