Hashmap 101


title: HashMap 101 search: true tags:

  • Java toc: true toc_label: “My Table of Contents” toc_icon: “cog” classes: wide —

I perfer HashMap over List.

Why perfer hashmap over list?

performance //todo

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

  1. From constructor
     @Test
     public void testInitialiseHashMap() {
         Map<String, Garden> gardenMap = new HashMap<>();
    
         Assert.notNull(gardenMap, "not null");
         Assert.isTrue(gardenMap.isEmpty(),"is empty");
     }
    
  2. From 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");
     }
    
  3. Avoid to use Anonymous Subclass to Create HashMap

    • It creates an extra sub-class and initializer block which is not good.
    • It creates memory leak problems.
    • new HashMap<String, Garden> you have to provide type inside of <>
    • Avoid using this wauy
     Map<String, Garden> map = new HashMap<String, Garden>() {
         {
             put("firstGarden", new Garden(1L, "first"));
             put("secondGarden", new Garden(2L, "second"));
             put("thirdGarden", new Garden(3L, "third"));
         }
     };
    

HashMap Methods

Put and Get

  1. 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");
     }
    
  2. 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");
     }
    
  3. 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

  1. Remove from key
  2. Cannot remove null as key
  3. 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

  1. ContainsKey
  2. 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

  1. 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");
     }
    
  2. 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");
     }
    
  3. 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");
     }
    
  4. 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 in Java

  1. Shallow copy copies reference. Change on one side will affect other side.
  2. Deep copy creates new object. Change on one side will NOT affect other side.

Shallow copy

  1. 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");
     }
    
  2. 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");
     }
    
  3. 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

  1. 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");
     }
    
  2. 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");
     }
    
  3. 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");
     }
    

References

  1. https://www.baeldung.com/java-copy-hashmap

Updated: