diff --git a/core-java-modules/core-java-collections-maps-4/README.md b/core-java-modules/core-java-collections-maps-4/README.md
new file mode 100644
index 0000000000..4449d3859b
--- /dev/null
+++ b/core-java-modules/core-java-collections-maps-4/README.md
@@ -0,0 +1,6 @@
+## Java Collections Cookbooks and Examples
+
+This module contains articles about Map data structures in Java.
+
+### Relevant Articles:
+- [Using a Custom Class as a Key in a Java HashMap](https://www.baeldung.com/custom-key-hashmap)
diff --git a/core-java-modules/core-java-collections-maps-4/pom.xml b/core-java-modules/core-java-collections-maps-4/pom.xml
new file mode 100644
index 0000000000..1835e3ceac
--- /dev/null
+++ b/core-java-modules/core-java-collections-maps-4/pom.xml
@@ -0,0 +1,34 @@
+
+
+ 4.0.0
+ core-java-collections-maps-4
+ 0.1.0-SNAPSHOT
+ core-java-collections-maps-4
+ jar
+
+
+ com.baeldung.core-java-modules
+ core-java-modules
+ 0.0.1-SNAPSHOT
+ ../pom.xml
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M5
+
+
+ !performance
+
+
+
+
+
+
+
diff --git a/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/maps/CoordinateKey.java b/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/maps/CoordinateKey.java
new file mode 100644
index 0000000000..34ae3d7952
--- /dev/null
+++ b/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/maps/CoordinateKey.java
@@ -0,0 +1,39 @@
+package com.baeldung.maps;
+
+import java.util.Objects;
+
+public class CoordinateKey {
+
+ private final int x;
+ private final int y;
+ private final int hashCode;
+
+ public CoordinateKey(int x, int y) {
+ this.x = x;
+ this.y = y;
+ this.hashCode = Objects.hash(x, y);
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ CoordinateKey that = (CoordinateKey) o;
+ return x == that.x && y == that.y;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.hashCode;
+ }
+}
diff --git a/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/maps/CoordinateMutableKey.java b/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/maps/CoordinateMutableKey.java
new file mode 100644
index 0000000000..08b329de3d
--- /dev/null
+++ b/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/maps/CoordinateMutableKey.java
@@ -0,0 +1,45 @@
+package com.baeldung.maps;
+
+import java.util.Objects;
+
+public class CoordinateMutableKey {
+
+ private int x;
+ private int y;
+
+ public CoordinateMutableKey(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public void setY(int y) {
+ this.y = y;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ CoordinateMutableKey that = (CoordinateMutableKey) o;
+ return x == that.x && y == that.y;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(x, y);
+ }
+}
diff --git a/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/maps/CoordinateSlowKey.java b/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/maps/CoordinateSlowKey.java
new file mode 100644
index 0000000000..9fe54bd9ea
--- /dev/null
+++ b/core-java-modules/core-java-collections-maps-4/src/main/java/com/baeldung/maps/CoordinateSlowKey.java
@@ -0,0 +1,13 @@
+package com.baeldung.maps;
+
+public class CoordinateSlowKey extends CoordinateKey {
+
+ public CoordinateSlowKey(int x, int y) {
+ super(x, y);
+ }
+
+ @Override
+ public int hashCode() {
+ return 1;
+ }
+}
diff --git a/core-java-modules/core-java-collections-maps-4/src/test/java/com/baeldung/maps/CoordinateKeyUnitTest.java b/core-java-modules/core-java-collections-maps-4/src/test/java/com/baeldung/maps/CoordinateKeyUnitTest.java
new file mode 100644
index 0000000000..8ee68c4a3d
--- /dev/null
+++ b/core-java-modules/core-java-collections-maps-4/src/test/java/com/baeldung/maps/CoordinateKeyUnitTest.java
@@ -0,0 +1,80 @@
+package com.baeldung.maps;
+
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import java.awt.*;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class CoordinateKeyUnitTest {
+
+ private Map pixels = new HashMap<>();
+
+ @Test
+ void testOptimalKey() {
+ // setup
+ CoordinateKey coord = new CoordinateKey(1, 2);
+ pixels.put(coord, Color.CYAN);
+ // read out color correctly
+ assertEquals(Color.CYAN, pixels.get(coord));
+ }
+
+ @Test
+ void testSlowKey() {
+ // setup
+ CoordinateKey coord = new CoordinateSlowKey(1, 2);
+ pixels.put(coord, Color.CYAN);
+ // read out color correctly
+ assertEquals(Color.CYAN, pixels.get(coord));
+ }
+
+ // Performance Test Parameters - change here
+ private static final int MAX_X = 100;
+ private static final int MAX_Y = 100;
+ private static final int COUNT_OF_QUERIES = 1000;
+ private static final int QUERY_X = 1;
+ private static final int QUERY_Y = 1;
+
+ @Tag("performance")
+ @Test
+ void testKeyPerformance() {
+ // generate some sample keys and values
+ for (int x = 0; x < MAX_X; x++) {
+ for (int y = 0; y < MAX_Y; y++) {
+ pixels.put(new CoordinateKey(x, y), new Color(x % 255, y % 255, (x + y) % 255));
+ }
+ }
+ // read out multiple times and measure time
+ CoordinateKey coord = new CoordinateKey(QUERY_X, QUERY_Y);
+ long t1 = System.currentTimeMillis();
+ for (int i = 0; i < COUNT_OF_QUERIES; i++) {
+ assertNotNull(pixels.get(coord));
+ }
+ long t2 = System.currentTimeMillis();
+ System.out.printf("Optimal key performance: %d ms%n", t2 - t1);
+ }
+
+ @Tag("performance")
+ @Test
+ void testSlowKeyPerformance() {
+ // generate some sample keys and values
+ for (int x = 0; x < MAX_X; x++) {
+ for (int y = 0; y < MAX_Y; y++) {
+ pixels.put(new CoordinateSlowKey(x, y), new Color(x % 255, y % 255, (x + y) % 255));
+ }
+ }
+ // read out multiple times and measure time
+ CoordinateKey coord = new CoordinateSlowKey(QUERY_X, QUERY_Y);
+ long t1 = System.currentTimeMillis();
+ for (int i = 0; i < COUNT_OF_QUERIES; i++) {
+ assertNotNull(pixels.get(coord));
+ }
+ long t2 = System.currentTimeMillis();
+ System.out.printf("Slow key performance: %d ms%n", t2 - t1);
+ }
+
+}
diff --git a/core-java-modules/core-java-collections-maps-4/src/test/java/com/baeldung/maps/CoordinateMutableKeyUnitTest.java b/core-java-modules/core-java-collections-maps-4/src/test/java/com/baeldung/maps/CoordinateMutableKeyUnitTest.java
new file mode 100644
index 0000000000..54a61be230
--- /dev/null
+++ b/core-java-modules/core-java-collections-maps-4/src/test/java/com/baeldung/maps/CoordinateMutableKeyUnitTest.java
@@ -0,0 +1,27 @@
+package com.baeldung.maps;
+
+import org.junit.jupiter.api.Test;
+
+import java.awt.*;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class CoordinateMutableKeyUnitTest {
+
+ @Test
+ void testKeyMutable() {
+ // setup
+ Map pixels = new HashMap<>();
+ CoordinateMutableKey coord = new CoordinateMutableKey(1, 2);
+ pixels.put(coord, Color.CYAN);
+ // read out color correctly
+ assertEquals(Color.CYAN, pixels.get(coord));
+ // change key's hashcode should result in null value
+ coord.setX(10);
+ assertNull(pixels.get(coord));
+ }
+
+}
diff --git a/core-java-modules/pom.xml b/core-java-modules/pom.xml
index b272d2aa13..5291c8c3ca 100644
--- a/core-java-modules/pom.xml
+++ b/core-java-modules/pom.xml
@@ -38,6 +38,7 @@
core-java-collections-maps
core-java-collections-maps-2
core-java-collections-maps-3
+ core-java-collections-maps-4
core-java-concurrency-2
core-java-concurrency-advanced
core-java-concurrency-advanced-2
@@ -138,4 +139,4 @@
5.6.2
-
\ No newline at end of file
+