DEV Community

loading...

Java Maps Cheat Sheet

scottshipp profile image scottshipp Originally published at code.scottshipp.com ・5 min read

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"
);

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}

Iterate a map

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

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

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);

This would output:

Washington=Olympia
Idaho=Boise
Oregon=Portland
Arizona=Phoenix
Nevada=Carson City
California=Sacramento

Convert to a collection of the maps values

Collection<String> capitalCities = statesToCapitals.values();

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]

Automatically retrieve a default value for missing entries

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

Outputs:

The capital of Wisconsin is Don't know

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
}

And if you have a collection of all US states:

Collection<State> usStates = usStates();

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()));

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));

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
}

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));

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]"

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");

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");

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 ...
}

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();

Then we just count them up:

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

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}

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)));

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));

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);

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]

More resources

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

_This post originally published at code.scottshipp.com

Discussion

pic
Editor guide
Collapse
fetahokey profile image
fetahokey

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

Collapse
scottshipp profile image
scottshipp Author

You're very welcome!