Skip to content

Add maps concept #2914

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions concepts/maps/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"blurb": "Maps are a collection of key value pairs.",
"authors": [
"kahgoh"
],
"contributors": []
}
171 changes: 171 additions & 0 deletions concepts/maps/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# About Maps

A **Map** is a data structure for storing key value pairs.
It is similar to dictionaries in other programming languages.
The [Map][map-javadoc] interface defines the operations you can make with a map.

## HashMap

Java has a number of different Map implementations.
[HashMap][hashmap-javadoc] is a commonly used one.

```java
// Make an instance
Map<String, Integer> fruitPrices = new HashMap<>();
```

~~~~exercism/note
When defining a `Map` variable, it is recommended to define the variable as a `Map` type rather than the specific type, as in the above example.
This practice makes it easy to change the `Map` implementation later.
~~~~

`HashMap` also has a copy constructor.

```java
// Make a copy of a map
Map<String, Integer> copy = new HashMap<>(fruitPrices);
```

Add entries to the map using [put][map-put-javadoc].

```java
fruitPrices.put("apple", 100);
fruitPrices.put("pear", 80);
// => { "apple" => 100, "pear" => 80 }
```

Only one value can be associated with each key.
Calling `put` with the same key will update the key's value.

```java
fruitPrices.put("pear", 40);
// => { "apple" => 100, "pear" => 40 }
```

Use [get][map-get-javadoc] to get the value for a key.

```java
fruitPrices.get("apple"); // => 100
```

Use [containsKey][map-containskey-javadoc] to see if the map contains a particular key.

```java
fruitPrices.containsKey("apple"); // => true
fruitPrices.containsKey("orange"); // => false
```

Remove entries with [remove][map-remove-javadoc].

```java
fruitPrices.put("plum", 90); // Add plum to map
fruitPrices.remove("plum"); // Removes plum from map
```

The [size][map-size-javadoc] method returns the number of entries.

```java
fruitPrices.size(); // Returns 2
```

You can use the [keys] or [values] methods to obtain the keys or the values in a Map as a Set or collection respectively.

```java
fruitPrices.keys(); // Returns "apple" and "pear" in a set
fruitPrices.values(); // Returns 100 and 80, in a Collection
```

## HashMap uses `hashCode` and `equals`

HashMaps uses the object's [hashCode][object-hashcode-javadoc] and [equals][object-equals-javadoc] method to work out where to store and how to retrieve the values for a key.
For this reason, it is important that their return values do not change between storing and getting them, otherwise the HashMap may not be able to find the value.

For example, lets say we have the following class that will be used as the key to a map:

```java
public class Stock {
private String name;

public void setName(String name) {
this.name = name;
}

@Override
public int hashCode() {
return Objects.hash(name);
}

@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (Objects.equals(Stock.class, obj.getClass()) && obj instanceof Stock other) {
return Objects.equals(name, other.name);
}
return false;
}
}
```

The `hashCode` and `equals` depend on the `name` field, which can be changed via `setName`.
Altering the `hashCode` can produce surprising results:

```java
Stock stock = new Stock();
stock.setName("Beanies");

Map<Stock, Integer> stockCount = new HashMap<>();
stockCount.put(stock, 80);

stockCount.get(stock); // Returns 80

Stock other = new Stock();
other.setName("Beanies");

stockCount.get(other); // Returns 80 because "other" and "stock" are equal

stock.setName("Choccies");
stockCount.get(stock); // Returns null because hashCode value has changed

stockCount.get(other); // Also returns null because "other" and "stock" are not equal

stock.setName("Beanies");
stockCount.get(stock); // HashCode restored, so returns 80 again

stockCount.get(other); // Also returns 80 again because "other" and "stock" are back to equal
```

## Map.of and Map.copyOf

Another common way to create maps is to use [Map.of][map-of-javadoc] or [Map.ofEntries][map-ofentries-javadoc].

```java
// Using Map.of
Map<String, Integer> temperatures = Map.of("Mon", 30, "Tue", 28, "Wed", 32);

// or using Map.ofEntries
Map<String, Integer> temperatures2 = Map.ofEntries(Map.entry("Mon", 30, "Tue", 28, "Wed", 32));
```

Unlike `HashMap`, they populate the map upfront and become read-only once created.
[Map.copyOf][map-copyof-javadoc] makes a read-only copy of a map.

```java
Map<String, Integer> readOnlyFruitPrices = Map.copyOf(fruitPrices);
```

Calling methods like `put`, `remove` or `clear` results in an `UnsupportedOperationException`.

[map-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html
[hashmap-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/HashMap.html
[map-put-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#put(K,V)
[map-get-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#get(java.lang.Object)
[map-containskey-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#containsKey(java.lang.Object)
[map-remove-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#remove(java.lang.Object)
[map-size-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#size()
[map-of-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#of()
[map-ofentries-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#ofEntries(java.util.Map.Entry...)
[map-copyof-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#copyOf(java.util.Map)
[object-hashcode-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Object.html#hashCode()
[object-equals-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Object.html#equals(java.lang.Object)
70 changes: 70 additions & 0 deletions concepts/maps/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Introduction

A **Map** is a data structure for storing key value pairs.
It is similar to dictionaries in other programming languages.
The [Map][map-javadoc] interface defines operations on a map.

Java has a number of different Map implementations.
[HashMap][hashmap-javadoc] is a commonly used one.

```java
// Make an instance
Map<String, Integer> fruitPrices = new HashMap<>();
```

Add entries to the map using [put][map-put-javadoc].

```java
fruitPrices.put("apple", 100);
fruitPrices.put("pear", 80);
// => { "apple" => 100, "pear" => 80 }
```

Only one value can be associated with each key.
Calling `put` with the same key will update the key's value.

```java
fruitPrices.put("pear", 40);
// => { "apple" => 100, "pear" => 40 }
```

Use [get][map-get-javadoc] to get the value for a key.

```java
fruitPrices.get("apple"); // => 100
```

Use [containsKey][map-containskey-javadoc] to see if the map contains a particular key.

```java
fruitPrices.containsKey("apple"); // => true
fruitPrices.containsKey("orange"); // => false
```

Remove entries with [remove][map-remove-javadoc].

```java
fruitPrices.put("plum", 90); // Add plum to map
fruitPrices.remove("plum"); // Removes plum from map
```

The [size][map-size-javadoc] method returns the number of entries.

```java
fruitPrices.size(); // Returns 2
```

You can use the [keys] or [values] methods to obtain the keys or the values in a Map as a Set or collection respectively.studentScores

```java
fruitPrices.keys(); // Returns "apple" and "pear" in a set
fruitPrices.values(); // Returns 100 and 80, in a Collection
```

[map-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html
[hashmap-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/HashMap.html
[map-put-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#put(K,V)
[map-get-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#get(java.lang.Object)
[map-containskey-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#containsKey(java.lang.Object)
[map-remove-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#remove(java.lang.Object)
[map-size-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#size()
10 changes: 10 additions & 0 deletions concepts/maps/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"url": "https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html",
"description": "Interface Map documentation"
},
{
"url": "https://dev.java/learn/api/collections-framework/maps/",
"description": "Using Maps to Store Key Value Pairs"
}
]
39 changes: 33 additions & 6 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,19 @@
"generic-types"
],
"status": "beta"
},
{
"slug": "international-calling-connoisseur",
"name": "International Calling Connoisseur",
"uuid": "03506c5a-601a-42cd-b037-c310208de84d",
"concepts": [
"maps"
],
"prerequisites": [
"classes",
"foreach-loops",
"generic-types"
]
}
],
"practice": [
Expand Down Expand Up @@ -989,7 +1002,8 @@
"practices": [],
"prerequisites": [
"if-else-statements",
"for-loops"
"for-loops",
"maps"
],
"difficulty": 5
},
Expand Down Expand Up @@ -1092,7 +1106,8 @@
"practices": [],
"prerequisites": [
"for-loops",
"arrays"
"arrays",
"maps"
],
"difficulty": 5
},
Expand Down Expand Up @@ -1144,7 +1159,8 @@
"practices": [],
"prerequisites": [
"chars",
"exceptions"
"exceptions",
"maps"
],
"difficulty": 6
},
Expand Down Expand Up @@ -1205,6 +1221,7 @@
"practices": [],
"prerequisites": [
"foreach-loops",
"maps",
"strings"
],
"difficulty": 6
Expand Down Expand Up @@ -1272,6 +1289,7 @@
"uuid": "38a405e8-619d-400f-b53c-2f06461fdf9d",
"practices": [],
"prerequisites": [
"maps",
"strings"
],
"difficulty": 6
Expand Down Expand Up @@ -1447,7 +1465,8 @@
"uuid": "2e760ae2-fadd-4d31-9639-c4554e2826e9",
"practices": [],
"prerequisites": [
"enums"
"enums",
"maps"
],
"difficulty": 7
},
Expand Down Expand Up @@ -1495,7 +1514,8 @@
"chars",
"if-else-statements",
"lists",
"for-loops"
"for-loops",
"maps"
],
"difficulty": 7
},
Expand Down Expand Up @@ -1556,7 +1576,8 @@
"prerequisites": [
"arrays",
"strings",
"if-else-statements"
"if-else-statements",
"maps"
],
"difficulty": 7
},
Expand Down Expand Up @@ -1703,6 +1724,7 @@
"exceptions",
"for-loops",
"if-else-statements",
"maps",
"numbers"
],
"difficulty": 8
Expand Down Expand Up @@ -1905,6 +1927,11 @@
"slug": "lists",
"name": "Lists"
},
{
"uuid": "2f6fdedb-a0ac-4bab-92d6-3be61520b9bc",
"slug": "maps",
"name": "Maps"
},
{
"uuid": "54118389-9c01-431b-a850-f47da498f845",
"slug": "method-overloading",
Expand Down
Loading