Java Maps Cheat Sheet
Tags: java
Published: 2020-02-19 18:33:04

---
title: Java Maps Cheat Sheet
published: true
description: There are a lot of online (and elsewhere) resources that don't represent the current Java Map interface, but a deprecated version.
tags: java
---

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
```java
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
```java
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:

```java
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
```java
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
```java
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:

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

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

And if you have a collection of all US states:

```java
Collection<State> usStates = usStates();
```

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

```java
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."

```java
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:

```java
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:

```java
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:

```java
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:

```java
// 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:

```java
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:

```java
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:

```java
Collection<Phone> phones = phonesOnAmazon();
```

Then we just count them up:
```java
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.

```java
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..].

```java
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:

```java
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!

* [Map interface javadoc](https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/util/Map.html)

* [Map.Entry javadoc](https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/util/Map.Entry.html)

* [Streams javadoc](https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/util/stream/package-summary.html)

* [Collectors javadoc](https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/util/stream/Collectors.html)

* Book: Kousen, K. A. (2017). _Modern Java recipes: simple solutions to difficult problems in Java 8 and 9._