Java Reference
In-Depth Information
Listing 3-8. A Library instance method to provided featured book messages using Map.computeIfAbsent
public Map<Book.Genre, Integer> getGenreCounts(Book.Genre... genres) {
Map<Book.Genre, Integer> toReturn = new HashMap<>();
Arrays.asList(genres).forEach(genre -> toReturn.put(genre, 0));
getBooks().forEach(book ->
toReturn.computeIfPresent(book.getGenre(), (key, count) -> count + 1 )
);
return toReturn;
}
It is also possible to use a single manipulator method to handle both missing and existing values. This
method is simply called compute , and the lambda is applied with the current value for that key: if the key was
not in the map before the method was called, the lambda is passed null as the value. If the lambda passed in
does not return null , then the compute method is effectively the same as this:
map.put(key, f(key, map.get(key));
Obviously, this adds some complexity to the code, because you have to handle both the case when the
key is already defined and the case when it is not. In exchange for handling this complexity, however, you
get maximum flexibility. If we wanted to extend our code in Listing 3-6 to provide all the default message
functionality that we implemented back in Listing 3-5, then we could use the compute method, and it would
look like Listing 3-9.
Listing 3-9. A Library instance method to provide featured book messages using Map.compute
public String getFeaturedBookMessage(final Book featuredBook) {
return getFeaturedBooks().compute(featuredBook, (book, msg) -> {
// Set a default message
if (msg == null || msg.isEmpty()) {
msg = "Featured " + book.getGenre().toString().toLowerCase()
+ " book by " + book.getAuthor();
}
// Remove trailing periods unless they are a bad elipsis
if (msg.endsWith(".") && !msg.endsWith("...")) {
msg = msg.substring(0, msg.length() - 1);
}
return msg;
}
);
}
The final manipulation API in the Map is merge . This method has an implementation that seems strange
at first: it takes a key, a value, and a “remapping function.” If the key is not already assigned, it uses the value;
if the key is assigned, it passes the argument value and the current value in the map into the remapping
function and assigns that to the key. After all that, the method returns the new value mapped to the given
key. As arcane as it may seem, this strange dance is exactly what you want for tracking counts in the map:
you pass in the key, and pass in the count as the value, and then pass in addition as your lambda. It also
works for tracking bitmasks, concatenating lists, and any other time that you are merging together values
based on a key. In Listing 3-10, we provide a general genre count method based on this approach.
 
Search WWH ::




Custom Search