diff --git a/core-java-modules/core-java-collections-maps-3/pom.xml b/core-java-modules/core-java-collections-maps-3/pom.xml index f547968b22..577ad58255 100644 --- a/core-java-modules/core-java-collections-maps-3/pom.xml +++ b/core-java-modules/core-java-collections-maps-3/pom.xml @@ -15,7 +15,11 @@ - + + org.openjdk.jmh + jmh-core + ${jmh-core.version} + diff --git a/core-java-modules/core-java-collections-maps-3/src/main/java/com/baeldung/map/concurrenthashmap/MapPerformanceComparison.java b/core-java-modules/core-java-collections-maps-3/src/main/java/com/baeldung/map/concurrenthashmap/MapPerformanceComparison.java new file mode 100644 index 0000000000..c788dbc27d --- /dev/null +++ b/core-java-modules/core-java-collections-maps-3/src/main/java/com/baeldung/map/concurrenthashmap/MapPerformanceComparison.java @@ -0,0 +1,96 @@ +package com.baeldung.map.concurrenthashmap; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; + +@Fork(5) +@Threads(10) +@Warmup(iterations = 5) +@State(Scope.Benchmark) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class MapPerformanceComparison { + + private int TEST_NO_ITEMS; + + public static void main(String[] args) throws Exception { + org.openjdk.jmh.Main.main(args); + } + + @Setup + public void setup() { + TEST_NO_ITEMS = 1000; + } + + @Benchmark + public void randomReadAndWriteSynchronizedMap() { + Map map = Collections.synchronizedMap(new HashMap()); + performReadAndWriteTest(map); + } + + @Benchmark + public void randomReadAndWriteConcurrentHashMap() { + Map map = new ConcurrentHashMap<>(); + performReadAndWriteTest(map); + } + + private void performReadAndWriteTest(final Map map) { + for (int i = 0; i < TEST_NO_ITEMS; i++) { + Integer randNumber = (int) Math.ceil(Math.random() * TEST_NO_ITEMS); + map.get(String.valueOf(randNumber)); + map.put(String.valueOf(randNumber), randNumber); + } + } + + @Benchmark + public void randomWriteSynchronizedMap() { + Map map = Collections.synchronizedMap(new HashMap()); + performWriteTest(map); + } + + @Benchmark + public void randomWriteConcurrentHashMap() { + Map map = new ConcurrentHashMap<>(); + performWriteTest(map); + } + + private void performWriteTest(final Map map) { + for (int i = 0; i < TEST_NO_ITEMS; i++) { + Integer randNumber = (int) Math.ceil(Math.random() * TEST_NO_ITEMS); + map.put(String.valueOf(randNumber), randNumber); + } + } + + @Benchmark + public void randomReadSynchronizedMap() { + Map map = Collections.synchronizedMap(new HashMap()); + performReadTest(map); + } + + @Benchmark + public void randomReadConcurrentHashMap() { + Map map = new ConcurrentHashMap<>(); + performReadTest(map); + } + + private void performReadTest(final Map map) { + for (int i = 0; i < TEST_NO_ITEMS; i++) { + Integer randNumber = (int) Math.ceil(Math.random() * TEST_NO_ITEMS); + map.get(String.valueOf(randNumber)); + } + } +} diff --git a/core-java-modules/core-java-collections-maps-3/src/test/java/com/baeldung/map/concurrenthashmap/ConcurrentModificationErrorUnitTest.java b/core-java-modules/core-java-collections-maps-3/src/test/java/com/baeldung/map/concurrenthashmap/ConcurrentModificationErrorUnitTest.java new file mode 100644 index 0000000000..4264e613f2 --- /dev/null +++ b/core-java-modules/core-java-collections-maps-3/src/test/java/com/baeldung/map/concurrenthashmap/ConcurrentModificationErrorUnitTest.java @@ -0,0 +1,70 @@ +package com.baeldung.map.concurrenthashmap; + +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; + +import org.junit.Assert; +import org.junit.Test; + +public class ConcurrentModificationErrorUnitTest { + + @Test(expected = ConcurrentModificationException.class) + public void whenRemoveAndAddOnHashMap_thenConcurrentModificationError() { + Map map = new HashMap<>(); + map.put(1, "baeldung"); + map.put(2, "HashMap"); + Map synchronizedMap = Collections.synchronizedMap(map); + Iterator> iterator = synchronizedMap.entrySet().iterator(); + while (iterator.hasNext()) { + synchronizedMap.put(3, "Modification"); + iterator.next(); + } + } + + @Test(expected = ConcurrentModificationException.class) + public void whenRemoveAndAddOnTreeMap_thenConcurrentModificationError() { + Map map = new TreeMap<>(); + map.put(1, "baeldung"); + map.put(2, "HashMap"); + Map synchronizedMap = Collections.synchronizedMap(map); + Iterator> iterator = synchronizedMap.entrySet().iterator(); + while (iterator.hasNext()) { + synchronizedMap.put(3, "Modification"); + iterator.next(); + } + } + + @Test(expected = ConcurrentModificationException.class) + public void whenRemoveAndAddOnLinkedHashMap_thenConcurrentModificationError() { + Map map = new LinkedHashMap<>(); + map.put(1, "baeldung"); + map.put(2, "HashMap"); + Map synchronizedMap = Collections.synchronizedMap(map); + Iterator> iterator = synchronizedMap.entrySet().iterator(); + while (iterator.hasNext()) { + synchronizedMap.put(3, "Modification"); + iterator.next(); + } + } + + @Test + public void whenRemoveAndAddOnConcurrentHashMap_thenNoError() { + Map map = new ConcurrentHashMap<>(); + map.put(1, "baeldung"); + map.put(2, "HashMap"); + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + map.put(3, "Modification"); + iterator.next(); + } + + Assert.assertEquals(3, map.size()); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-collections-maps-3/src/test/java/com/baeldung/map/concurrenthashmap/NullAllowInMapUnitTest.java b/core-java-modules/core-java-collections-maps-3/src/test/java/com/baeldung/map/concurrenthashmap/NullAllowInMapUnitTest.java new file mode 100644 index 0000000000..594e8ec3b4 --- /dev/null +++ b/core-java-modules/core-java-collections-maps-3/src/test/java/com/baeldung/map/concurrenthashmap/NullAllowInMapUnitTest.java @@ -0,0 +1,73 @@ +package com.baeldung.map.concurrenthashmap; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; + +import org.junit.Assert; +import org.junit.Test; + +public class NullAllowInMapUnitTest { + + @Test + public void givenHashMapBackedSynchronizedMap_whenNullAsKey_thenNoError() { + Map map = Collections + .synchronizedMap(new HashMap()); + map.put(null, 1); + Assert.assertTrue(map.get(null).equals(1)); + } + + + @Test(expected = NullPointerException.class) + public void givenTreeMapBackedSynchronizedMap_whenNullAsKey_thenException() { + Map map = Collections.synchronizedMap(new TreeMap()); + map.put(null, 1); + Assert.assertTrue(map.get(null).equals(1)); + } + + @Test + public void givenLinkedHashMapBackedSynchronizedMap_whenNullAsKey_thenNoError() { + Map map = Collections + .synchronizedMap(new LinkedHashMap()); + map.put(null, 1); + Assert.assertTrue(map.get(null).equals(1)); + } + + @Test(expected = NullPointerException.class) + public void givenConcurrentHasMap_whenNullAsKey_thenException() { + Map map = new ConcurrentHashMap<>(); + map.put(null, 1); + } + + @Test + public void givenHashMapBackedSynchronizedMap_whenNullAsValue_thenNoError() { + Map map = Collections.synchronizedMap(new HashMap()); + map.put("1", null); + Assert.assertNull(map.get("1")); + } + + @Test + public void givenTreeMapBackedSynchronizedMap_whenNullAsValue_thenNoError() { + Map map = Collections.synchronizedMap(new TreeMap()); + map.put("1", null); + Assert.assertNull(map.get("1")); + } + + @Test + public void givenLinkedHashMapBackedSynchronizedMap_whenNullAsValue_thenNoError() { + Map map = Collections + .synchronizedMap(new LinkedHashMap()); + map.put("1", null); + Assert.assertNull(map.get("1")); + } + + @Test(expected = NullPointerException.class) + public void givenConcurrentHasMap_whenNullAsValue_thenException() { + Map map = new ConcurrentHashMap<>(); + map.put("1", null); + } + +} \ No newline at end of file