HashMap 101
HashMap is fast.
What is HashMap in Java?
key-> value
null as a key
mapDemo.get(null);
key cannot be dumplicate
value can be duplicate
mapDemo.put(1, A); mapDemo.put(2, A);
equals() and hashCode()
Initailise a HashMap
see: Initialize a HashMap in Java from Bealdung.
-
From a static block (recommanded)
static HashMap is mutable, you can add or remove element later.
public static Map<String, String> articleMapOne; static { articleMapOne = new HashMap<>(); articleMapOne.put("ar01", "Intro to Map"); articleMapOne.put("ar02", "Some article"); }
Map<String, String> myMap = createMap(); private static Map<String, String> createMap() { Map<String,String> myMap = new HashMap<String,String>(); myMap.put("a", "b"); myMap.put("c", "d"); return myMap; }
-
Initialize the map using the double-brace syntax (avoid)
- It generates an additional class which increases memory consumption, disk space consumption and startup-time
- In case of a non-static method: It holds a reference to the object the creating method was called upon. That means the object of the outer class cannot be garbage collected while the created map object is still referenced, thus blocking additional memory
Map<String, String> doubleBraceMap = new HashMap<String, String>() ;
-
Use
Collections.singletonMap
Immutable, throwsjava.lang.UnsupportedOperationException
public static Map<String, String> createSingletonMap() { return Collections.singletonMap("username1", "password1"); }
-
Use Java8 Stream
Collectors.toMap
@Test public void testCollectorsToMap() { Map<String, Garden> gardenMap = Stream.of(new Object[][]{ {"firstGarden", new Garden(1L, "first")}, {"secondGarden", new Garden(2L, "second")} }).collect(Collectors.toMap(data -> (String)data[0], data -> (Garden)data[1])); Assert.isTrue(gardenMap.size() == 2,"size is 2"); Assert.isTrue(gardenMap.**get**("firstGarden").getName().equals("first"), "first garden name is first"); }
-
Use
Map.of()
- supports only a maximum of 10 key-value pairs
- immutable
Map<String, String> map = Map.of("key1","value1", "key2", "value2");
Adding elements after initialization, you need to create a new HashMap:
Map<String, String> map = new HashMap<String, String> ( Map.of("key1","value1", "key2", "value2"));
-
Map.ofEntries()
- It’s similar to the
Map.of()
but has no limitations on the number of key-value pairs - immutable
- do not allow null keys or duplicate keys
Map<String, String> map = Map.ofEntries( new AbstractMap.SimpleEntry<String, String>("name", "John"), new AbstractMap.SimpleEntry<String, String>("city", "budapest"), new AbstractMap.SimpleEntry<String, String>("zip", "000000"), new AbstractMap.SimpleEntry<String, String>("home", "1231231231") );
Adding elements after initialization, you need to create a new HashMap:
Map<String, String> map2 = new HashMap<String, String> ( Map.ofEntries( new AbstractMap.SimpleEntry<String, String>("name", "John"), new AbstractMap.SimpleEntry<String, String>("city", "budapest")));
- It’s similar to the
HashMap Methods
Put and Get
-
Put and Get
@Test public void testHashMapPutAndGet() { Map<String, Garden> gardenMap = new HashMap<>(); gardenMap.put("firstGarden", new Garden(1L, "first")); final Garden firstGarden = gardenMap.get("firstGarden"); Assert.isTrue(firstGarden.getId().equals(1L), "first garden id is 1L"); Assert.isTrue(firstGarden.getName().equals("first"), "first garden name is first"); }
-
putIfAbsent()
@Test public void testHashMapPutIfAbsent() { Map<String, Garden> gardenMap = new HashMap<>(); gardenMap.putIfAbsent("firstGarden", new Garden(1L, "first")); gardenMap.putIfAbsent("firstGarden", new Garden(2L, "second")); final Garden firstGarden = gardenMap.get("firstGarden"); Assert.isTrue(firstGarden.getId().equals(1L), "first garden id is 1L"); Assert.isTrue(firstGarden.getName().equals("first"), "first garden name is first"); }
-
getOrDefault()
@Test public void testHashMapGetOrDefault() { Map<String, Garden> gardenMap = new HashMap<>(); gardenMap.putIfAbsent("firstGarden", new Garden(1L, "first")); final Garden secondGarden = gardenMap.get("second"); Assert.isNull(secondGarden, "It does not exists yet"); Garden defaultGarden = new Garden(0L, "default"); final Garden second = gardenMap.getOrDefault("second", defaultGarden); Assert.isTrue(second.getId().equals(0L), "default garden id is 0L"); Assert.isTrue(second.getName().equals("default"), "default garden name is default"); }
remove a value
- Remove from key
- Cannot remove
null
as key -
Can remove an entrySet,
null
as key@Test public void testHashMapRemove() { Map<String, Garden> gardenMap = new HashMap<>(); gardenMap.put("firstGarden", new Garden(1L, "first")); Garden nullGarden = new Garden(2L, "null"); gardenMap.put(null, nullGarden); gardenMap.remove("firstGarden"); Assert.isTrue(gardenMap.size() == 1, "1 left"); gardenMap.remove(null); //java.lang.IllegalArgumentException: cannot remove null gardenMap.remove(null, nullGarden); Assert.isTrue(gardenMap.size() == 0, "finally gone"); }
Contains
- ContainsKey
-
ContainsValue
@Test public void testHashMapContains() { Map<String, Garden> gardenMap = new HashMap<>(); gardenMap.put("firstGarden", null); final Garden nullGarden = new Garden(0L , "null"); gardenMap.put(null, nullGarden); Assert.isTrue( gardenMap.containsKey("firstGarden"), "contains firstGarden key"); Assert.isTrue( gardenMap.containsValue(nullGarden), "contains nullGarden"); Assert.isTrue( gardenMap.containsKey(null), "contains null key"); Assert.isTrue( gardenMap.containsValue(null), "contains null value"); }
merge() and compute()
//todo
Iterating over a HashMap
-
iterating keys
@Test public void testHashMapIterateKeys() { Map<String, Garden> gardenMap = new HashMap<>(); final Garden nullGarden = new Garden(0L , "null"); final Garden firstGarden = new Garden(1L , "first"); final Garden secondGarden = new Garden(2L , "second"); gardenMap.put(null, nullGarden); gardenMap.put("firstGarden", firstGarden); gardenMap.put("secondGarden", secondGarden); for (String key : gardenMap.keySet()) { if (key == null) { Garden theGarden = gardenMap.get(null); Assert.isTrue( theGarden == nullGarden, "got null garden"); gardenMap.put(null, firstGarden); } } Assert.isTrue( gardenMap.get(null) == firstGarden, "null garden is replaced"); }
-
iterating values
@Test public void testHashMapIterateValues() { Map<String, Garden> gardenMap = new HashMap<>(); final Garden nullGarden = new Garden(0L , "null"); final Garden firstGarden = new Garden(1L , "first"); final Garden secondGarden = new Garden(2L , "second"); gardenMap.put(null, nullGarden); gardenMap.put("firstGarden", firstGarden); gardenMap.put("secondGarden", secondGarden); for (Garden garden : gardenMap.values()) { if (garden == nullGarden) { garden.setId(3L); } } Assert.isTrue( gardenMap.get(null).getId() == 3L, "null garden is updated"); }
-
iterating entries
@Test public void testHashMapIterateEntries() { Map<String, Garden> gardenMap = new HashMap<>(); final Garden nullGarden = new Garden(0L , "null"); final Garden firstGarden = new Garden(1L , "first"); final Garden secondGarden = new Garden(2L , "second"); gardenMap.put(null, nullGarden); gardenMap.put("firstGarden", firstGarden); gardenMap.put("secondGarden", secondGarden); for (Map.Entry<String, Garden> entry : gardenMap.entrySet()) { if (entry.getKey() == null) { Assert.isTrue(entry.getValue() == nullGarden, "here is the null garden"); entry.getValue().setId(3L); } } Assert.isTrue( gardenMap.get(null).getId() == 3L, "null garden is updated"); }
-
iterator avoids
ConcurrentModificationException
@Test public void testHashMapIterator() { Map<String, Garden> gardenMap = new HashMap<>(); final Garden nullGarden = new Garden(0L , "null"); final Garden firstGarden = new Garden(1L , "first"); final Garden secondGarden = new Garden(2L , "second"); gardenMap.put(null, nullGarden); gardenMap.put("firstGarden", firstGarden); gardenMap.put("secondGarden", secondGarden); Iterator it = gardenMap.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); if (entry.getKey() == null) { Garden theGarden = (Garden) entry.getValue(); Assert.isTrue( theGarden.getId() == 0L, "null garden id is 0"); it.remove(); // avoids a ConcurrentModificationException } } Assert.isTrue( gardenMap.size() == 2, "null garden is removed"); }
Copying a HashMap
- Shallow copy copies reference. Change on one side will affect other side.
- Deep copy creates new object. Change on one side will NOT affect other side.
Shallow copy
-
Using the HashMap Constructor
@Test public void testHashMapConstructorCopy() { Map<String, Garden> gardenMap = new HashMap<>(); final Garden nullGarden = new Garden(0L , "null"); final Garden firstGarden = new Garden(1L , "first"); final Garden secondGarden = new Garden(2L , "second"); gardenMap.put(null, nullGarden); gardenMap.put("firstGarden", firstGarden); gardenMap.put("secondGarden", secondGarden); HashMap<String, Garden> shallowCopy = new HashMap<>(gardenMap); Assert.isTrue( shallowCopy.size() == 3, "shallow copy has same size"); nullGarden.setId(3L); Assert.isTrue( shallowCopy.get(null).getId() == 3L, "shallow copy is also updated"); }
-
Using
Map.put()
copy@Test public void testHashMapPutCopy() { Map<String, Garden> gardenMap = new HashMap<>(); final Garden nullGarden = new Garden(0L , "null"); final Garden firstGarden = new Garden(1L , "first"); final Garden secondGarden = new Garden(2L , "second"); gardenMap.put(null, nullGarden); gardenMap.put("firstGarden", firstGarden); gardenMap.put("secondGarden", secondGarden); HashMap<String, Garden> shallowCopy = new HashMap<>(); gardenMap.entrySet() .forEach(entry -> shallowCopy.put(entry.getKey(), entry.getValue())); Assert.isTrue( shallowCopy.size() == 3, "shallow copy has same size"); nullGarden.setId(3L); Assert.isTrue( shallowCopy.get(null).getId() == 3L, "shallow copy is also updated"); }
-
Using
Map.putAll()
copy@Test public void testHashMapPutAllCopy() { Map<String, Garden> gardenMap = new HashMap<>(); final Garden nullGarden = new Garden(0L , "null"); final Garden firstGarden = new Garden(1L , "first"); final Garden secondGarden = new Garden(2L , "second"); gardenMap.put(null, nullGarden); gardenMap.put("firstGarden", firstGarden); gardenMap.put("secondGarden", secondGarden); HashMap<String, Garden> shallowCopy = new HashMap<>(); shallowCopy.putAll(gardenMap); Assert.isTrue( shallowCopy.size() == 3, "shallow copy has same size"); nullGarden.setId(3L); Assert.isTrue( shallowCopy.get(null).getId() == 3L, "shallow copy is also updated"); }
Deep copy
//todo Now, Java doesn’t have any built-in deep copy implementations. So to make a deep copy, either we can override the clone() method or use a serialization-deserialization technique.
Apache Commons has SerializationUtils with a clone() method to create a deep copy. For this, any class to be included in deep copy must implement the Serializable interface:
Linkedhashmap
//todo
Concurrent HashMap
-
ConcurrentHashMap key cannot be null
@Test public void testConcurrentHashMapKeyNull() { Map<String, Garden> gardenMap = new ConcurrentHashMap<>(); try { gardenMap.put(null, new Garden()); } catch (NullPointerException ex) { } Assert.isTrue(gardenMap.size() == 0, "still empty"); }
-
ConcurrentHashMap allows you add and remove an entry while iterating. HashMap won’t allow.
@Test public void testConcurrentHashMapRemove() { Map<String, Garden> gardenMap = new ConcurrentHashMap<>(); gardenMap.put("firstGarden", new Garden(1L , "first")); gardenMap.put("secondGarden", new Garden(2L , "second")); gardenMap.entrySet() .forEach(entry -> { if (entry.getKey().equalsIgnoreCase("firstGarden")) { gardenMap.remove("firstGarden"); } }); Assert.isTrue( gardenMap.size() == 1, "first garden is gone"); }
-
ConcurrentHashMap allows you to add or move entries in one thred, and you can see it in another thread.
@Test public void testConcurrentHashMapTwoThread() throws Exception { Map<String, Garden> gardenMap = new ConcurrentHashMap<>(); gardenMap.put("firstGarden", new Garden(1L, "first")); gardenMap.put("secondGarden", new Garden(2L, "second")); Thread thread1 = new Thread(new Runnable() { public void run() { try { Thread.sleep(50); gardenMap.put("third garden", new Garden()); } catch (InterruptedException e) { } } }); Thread thread2 = new Thread(new Runnable() { public void run() { try { Thread.sleep(70); Assert.isTrue(gardenMap.size() == 3, "we got one from other theads"); } catch (InterruptedException e) { } } }); thread1.start(); thread2.start(); Thread.sleep(100); Assert.isTrue(gardenMap.size() == 3, "3 entries now"); }