Java HashMap


The HashMap class in Java is one of the most widely used implementations of the Map interface. It allows the storage of key-value pairs, where each key is associated with exactly one value. HashMap is part of the java.util package and provides fast lookups, insertions, and deletions based on the hash code of the keys. It is a flexible and efficient data structure commonly used in everyday Java programming tasks, such as caching, counting frequencies, and implementing associative arrays.


What is a HashMap in Java?

A HashMap is a hash table-based implementation of the Map interface. It stores key-value pairs, where each key is unique, and each key is associated with exactly one value. The HashMap provides constant-time performance for basic operations like get() and put() on average, assuming that the hash function distributes the keys uniformly.

Key features of HashMap:

  • Unordered: The elements in a HashMap are not stored in any particular order. The order in which the entries are retrieved is not guaranteed.
  • Allows Null: A HashMap allows one null key and multiple null values.
  • Thread-Unsafe: HashMap is not synchronized, meaning it is not thread-safe. If multiple threads are modifying a HashMap concurrently, it can lead to unpredictable behavior. You can use ConcurrentHashMap if thread-safety is needed.

Constructor of HashMap

The HashMap class offers several constructors to initialize the map:

HashMap()                 // Creates an empty map with the default initial capacity (16) and load factor (0.75).
HashMap(int initialCapacity)  // Creates a map with the specified initial capacity.
HashMap(int initialCapacity, float loadFactor)  // Creates a map with the specified initial capacity and load factor.
HashMap(Map<? extends K, ? extends V> m)  // Creates a map with the same mappings as the specified map.
  • initialCapacity: The initial capacity is the number of buckets in the map. The default is 16.
  • loadFactor: The load factor is the threshold at which the map will resize. The default is 0.75, meaning when the map is 75% full, it will resize.

Common Methods of HashMap

Here are some commonly used methods of the HashMap class:

  • put(K key, V value): Adds a key-value pair to the map. If the key already exists, the value is updated.

    map.put("apple", 1);
    map.put("banana", 2);
    
  • get(Object key): Retrieves the value associated with the specified key.

    int value = map.get("apple");  // Returns 1
    
  • containsKey(Object key): Checks if the map contains the specified key.

    boolean exists = map.containsKey("banana");  // Returns true
    
  • remove(Object key): Removes the key-value pair associated with the specified key.

    map.remove("banana");  // Removes the key "banana"
    
  • size(): Returns the number of key-value pairs in the map.

    int size = map.size();  // Returns the number of entries in the map
    
  • keySet(): Returns a set view of all the keys in the map.

    Set<String> keys = map.keySet();  // Returns a Set of all the keys
    
  • values(): Returns a collection view of all the values in the map.

    Collection<Integer> values = map.values();  // Returns a Collection of all the values
    
  • entrySet(): Returns a set view of all the key-value pairs (entries) in the map.

    Set<Map.Entry<String, Integer>> entries = map.entrySet();  // Returns a Set of Map.Entry
    
  • clear(): Removes all key-value pairs from the map.

    map.clear();  // Removes all entries in the map
    

Example 1: Basic Usage of HashMap

Here’s an example of how to use a HashMap in Java to store and retrieve key-value pairs:

import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        // Create a new HashMap
        Map<String, Integer> map = new HashMap<>();

        // Add some key-value pairs to the map
        map.put("apple", 1);
        map.put("banana", 2);
        map.put("orange", 3);

        // Retrieve values using keys
        System.out.println("Value for 'apple': " + map.get("apple"));  // Output: 1
        System.out.println("Value for 'banana': " + map.get("banana"));  // Output: 2

        // Check if the map contains a specific key
        if (map.containsKey("orange")) {
            System.out.println("'orange' is present in the map");
        }

        // Remove a key-value pair
        map.remove("banana");

        // Print all entries in the map
        System.out.println("Entries in the map: ");
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + " -> " + entry.getValue());
        }
    }
}

Output:

Value for 'apple': 1
Value for 'banana': 2
'orange' is present in the map
Entries in the map: 
apple -> 1
orange -> 3

In this example:

  • We create a new HashMap and add key-value pairs using the put() method.
  • We retrieve values with the get() method.
  • We check if the map contains a certain key with containsKey().
  • We remove a key-value pair with the remove() method and print all entries using entrySet().

Example 2: HashMap with Custom Objects

You can use custom objects as keys or values in a HashMap. However, to use an object as a key, you must ensure that the object properly overrides the hashCode() and equals() methods to allow efficient searching and equality checking.

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

class Person {
    String name;
    int age;

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        return age == person.age && Objects.equals(name, person.name);
    }

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

public class HashMapCustomObjectExample {
    public static void main(String[] args) {
        Map<Person, String> personMap = new HashMap<>();

        // Create person objects
        Person person1 = new Person("John", 25);
        Person person2 = new Person("Alice", 30);

        // Add persons to the map with some data
        personMap.put(person1, "Engineer");
        personMap.put(person2, "Doctor");

        // Retrieve values using the custom objects as keys
        System.out.println(person1.name + " is a " + personMap.get(person1));  // Output: John is a Engineer
        System.out.println(person2.name + " is a " + personMap.get(person2));  // Output: Alice is a Doctor
    }
}

Output:

John is a Engineer
Alice is a Doctor

In this example:

  • Person objects are used as keys in the HashMap.
  • We override the equals() and hashCode() methods in the Person class to ensure proper functioning of the map when using custom objects as keys.

Performance Considerations

  • Time Complexity: The average time complexity for basic operations like get(), put(), and remove() is O(1), assuming a good distribution of hash codes.
  • Resize: If the HashMap exceeds the threshold determined by the initial capacity and load factor, it will resize automatically, which can cause some performance overhead.
  • Collision Handling: HashMap handles hash collisions (when multiple keys have the same hash code) using linked lists or binary trees, depending on the number of collisions.