DEV Community

scottshipp
scottshipp

Posted on • Updated on • Originally published at code.scottshipp.com

Java Maps Cheat Sheet

I thought I'd drop a couple of real-world examples of using the Map interface in Java, mostly because there are still a lot of old resources around that don't use modern Java. Hope it helps!

Create a map

Map<String, String> statesToCapitals = Map.of(
        "Washington", "Olympia",
        "Oregon", "Portland",
        "California", "Sacramento",
        "Idaho", "Boise",
        "Nevada", "Carson City",
        "Arizona", "Phoenix"
);
Enter fullscreen mode Exit fullscreen mode

It's important to remember that maps created this way are immutable, and null can't be used as a key!

This creates a map as follows:

{Nevada=Carson City, California=Sacramento, Washington=Olympia, Idaho=Boise, Oregon=Portland, Arizona=Phoenix}
Enter fullscreen mode Exit fullscreen mode

Iterate a map

statesToCapitals.forEach((key, value) -> {
    System.out.println("The capital of " + key + " is " + value);
});
Enter fullscreen mode Exit fullscreen mode

This would output:

The capital of Washington is Olympia
The capital of Idaho is Boise
The capital of Oregon is Portland
The capital of Arizona is Phoenix
The capital of Nevada is Carson City
The capital of California is Sacramento

Enter fullscreen mode Exit fullscreen mode

Iterate Map entries

Besides the forEach method directly available on the map, which allows you to pass a function to apply to its keys and/or values, you can also stream its entries and work with the Map.Entry class.

For example, you can print the entries:

capitals.entrySet().stream().forEach(System.out::println);
Enter fullscreen mode Exit fullscreen mode

This would output:

Washington=Olympia
Idaho=Boise
Oregon=Portland
Arizona=Phoenix
Nevada=Carson City
California=Sacramento
Enter fullscreen mode Exit fullscreen mode

Convert to a collection of the maps values

Collection<String> capitalCities = statesToCapitals.values();
Enter fullscreen mode Exit fullscreen mode

Note that depending on the map implementation being used, you could receive the values in unsorted or sorted order.

So you'd have something like:

[Sacramento, Carson City, Phoenix, Portland, Boise, Olympia]
Enter fullscreen mode Exit fullscreen mode

Automatically retrieve a default value for missing entries

System.out.println("The capital of Wisconsin is " + statesToCapitals.getOrDefault("Wisconsin", "Don't know"));
Enter fullscreen mode Exit fullscreen mode

Outputs:

The capital of Wisconsin is Don't know
Enter fullscreen mode Exit fullscreen mode

Convert from a collection of objects to a lookup map for those objects

Let's say you started with a State object which represents a US state:

class State {
   public String name() { // ... etc

   public String capitalCity() { // ... etc
}
Enter fullscreen mode Exit fullscreen mode

And if you have a collection of all US states:

Collection<State> usStates = usStates();
Enter fullscreen mode Exit fullscreen mode

You can create a map to lookup each state by its name:

Map<String, State> nameToState = usStates.stream().collect(Collectors.toMap(State::name, Function.identity()));
Enter fullscreen mode Exit fullscreen mode

In other words, you can now get California with nameToState.get("California") and you will get the instance of State associated with California.

Convert from a collection of objects to a lookup map on one of its values

Assume the same as above, but this time, you want to go from states to capitals--the map's key will be a value from the object, its name, like "California" or "Wisconsin", and the map's value will also be a value from the object, its capital, like "Sacramento" or "Madison."

Map<String, String> statesToCapitals = usStates.stream().collect(Collectors.toMap(State::name, State::capitalCity));
Enter fullscreen mode Exit fullscreen mode

Handling maps of lists

Now let's consider if the State object also was able to return a collection of all major cities in a state:

class State {
   public String name() { // ... etc

   public String capitalCity() { // ... etc

   public List<String> majorCities() { // ... etc
}
Enter fullscreen mode Exit fullscreen mode

And you created a statesToCities map similar to the above with capitals:

Map<String, List<String>> statesToCities = usStates.stream().collect(Collectors.toMap(State::name, State::majorCities));
Enter fullscreen mode Exit fullscreen mode

The difference now is we have a Map<String, List<String>> instead of a simpler Map<String, String> like we had before. The "value" of the map is a list of cities. For example:

List<String> californiaCities = statesToCities.get("California");
System.out.println(californiaCities);
// outputs "[Sacramento, San Francisco, Los Angeles]"
Enter fullscreen mode Exit fullscreen mode

Add a value to the list in a Map of Lists

In "old school Java", you had to check if a key existed in the map so that you could handle initializing the list if it wasn't there:

// deprecated way of adding new values
Map<String, List<String>> statesToCities = statesAndCitiesMap();
if(!statesToCities.containsKey("Montana")) {
  statesToCities.put("Montana", new ArrayList<>());
}
statesToCities.get("Montana").add("Bozeman");
Enter fullscreen mode Exit fullscreen mode

Now you can use computeIfAbsent in place of get to insure you start with the initial empty list even if nothing is there to begin with:

statesToCities.computeIfAbsent("Montana", initialValue -> new ArrayList<>()).add("Bozeman");
Enter fullscreen mode Exit fullscreen mode

Create a count map

Here's a real world scenario that comes up all the time. What if we need a map to count the number of each type of something in a collection.

For this example, let's assume we have a Phone class with brand and model:

class Phone {
    public String brand() { // etc ...

    public String model() { // etc ...
}
Enter fullscreen mode Exit fullscreen mode

Examples would be "Apple iPhone 11" or "Google Pixel 4".

And a collection of phones, let's say all the phones that Amazon offers for sale:

Collection<Phone> phones = phonesOnAmazon();
Enter fullscreen mode Exit fullscreen mode

Then we just count them up:

Map<String, Long> numberOfPhonesPerBrand = phones.stream().collect(Collectors.groupingBy(Phone::brand, Collectors.counting()));
Enter fullscreen mode Exit fullscreen mode

Confused? The groupingBy collector returns a map with the first value passed as the key (Phone::brand) and the calculation produced by the passed function as the value. In this case, we wanted to count up every phone we saw for that brand, so we used Collectors.counting().

The map created by the above looks like this when output:

{Google=2, Apple=2}
Enter fullscreen mode Exit fullscreen mode

Create a count map by summing up some value in each object

To expand on the prior example, let's assume you want to find out how many phones Amazon has at any given time, per brand. We can't just count how many phones there are. Instead, for this example, we have to use the numberAvailable method from the Phone class, which tells us how many phones are available on Amazon right now.

Map<String, Integer> numOfPhonesPerBrand = phones.stream().collect(Collectors.groupingBy(Phone::brand, Collectors.summingInt(Phone::numberAvailable)));
Enter fullscreen mode Exit fullscreen mode

In addition to summingInt, there are collectors called summingLong and summingDouble which can add up values from methods returning long or double respectively.

Accumulate into a map of lists

Let's say you have a list of Phone objects and you want to create a Map<String, List<Phone>> representing a list of phones by brand. The key is a String and is the brand, such as "Apple" or "Samsung". The map value is a List<String> and is just a list of phones such as for Apple it would be ["Apple iPhone 11", "Apple iPhone 11 Pro", etc..].

Map<String, List<Phone>> phoneListByBrand = phones.stream().collect(Collectors.groupingBy(Phone::brand));
Enter fullscreen mode Exit fullscreen mode

Sort Map entries

The Map.Entry class has a number of its own static methods for use in map operations. You could, for example, sort the map before printing:

phonesByBrand.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(System.out::println);
Enter fullscreen mode Exit fullscreen mode

This will output:

Apple=[Apple iPhone 11, Apple iPhone 11 Pro]
Google=[Google Pixel 4, Google Pixel 4 XL]
Samsung=[Samsung Galaxy Note 10, Samsung Galaxy Fold]
Enter fullscreen mode Exit fullscreen mode

More resources

Check out these resources if you want to learn more about modern Java maps!

Top comments (2)

Collapse
 
fetahokey profile image
fetahokey

Thanks for the valuable effort.
illustration and explanation more than useful.

Collapse
 
scottshipp profile image
scottshipp

You're very welcome!