Java Map Interface

The Java map interface is a data structure that stores a collection of unique key-value pairs, where each key is distinct and corresponds to a single value. This interface is a part of the java.util package and is extensively utilized in Java development for organizing and retrieving data systematically. Maps are beneficial when there is a need to search for, modify, or remove elements based on a specific key.

Why Java Map Interface?

Here are a variety of situations and justifications in which leveraging the Map interface could prove advantageous:

Effective Data Retrieval: Maps leverage keys to access values selectively. When there is a need to establish a connection between different datasets that allows for efficient retrieval of relevant information based on specific keys, maps prove to be highly beneficial. For example, when dealing with a large dataset and needing to swiftly locate data related to particular keys like IDs, names, or codes, utilizing a map can facilitate rapid access to the required information.

Unique Keys: In maps, keys are distinct, ensuring each key is one-of-a-kind. This characteristic is useful in ensuring that each key corresponds to a single value. For instance, in a dictionary program, it is important to ensure that each word has a sole definition.

Java Maps offer versatility in the types of keys and values they can store. They allow for the storage of keys and values of various types. Keys can be any object that implements the hashCode and equals methods, while the values can also be any object. This flexibility enables you to represent diverse relationships between objects.

Maps are commonly employed for associative arrays, dictionaries, and symbol tables as associative data structures. They provide efficient storage and retrieval of data, where each element is uniquely identified by a specific key.

Caching and Memoization: Utilizing maps for caching and memoization is highly beneficial. Storing the outcomes of an expensive operation or a derivative of a specific operation in a map, associating them with the inputs or operation parameters, can be extremely advantageous. This approach allows for quick access to the cached map entry when encountering the same input again, eliminating the need to recalculate it entirely.

In a Map data structure, duplicate keys are not permitted, although duplicate values are allowed. Both HashMap and LinkedHashMap support the inclusion of null keys and values, whereas TreeMap does not permit the presence of any null key or value.

To iterate over the elements of a Map, it is necessary to transform it into a Set by utilizing either the keySet or entrySet method.

Class Description
HashMap HashMap is the implementation of Map, but it doesn't maintain any order.
LinkedHashMap LinkedHashMap is the implementation of Map. It inherits HashMap class. It maintains insertion order.
TreeMap TreeMap is the implementation of Map and SortedMap. It maintains ascending order.

Useful methods of Map interface

Method Description
V put(Object key, Object value) It is used to insert an entry in the map.
void putAll(Map map) It is used to insert the specified map in the map.
V putIfAbsent(K key, V value) It inserts the specified value with the specified key in the map only if it is not already specified.
V remove(Object key) It is used to delete an entry for the specified key.
boolean remove(Object key, Object value) It removes the specified values with the associated specified keys from the map.
Set keySet() It returns the Set view containing all the keys.
Set<Map.Entry<K,V>> entrySet() It returns the Set view containing all the keys and values.
void clear() It is used to reset the map.
V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) It is used to compute a mapping for the specified key and its current mapped value (or null if there is no current mapping).
V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) It is used to compute its value using the given mapping function, if the specified key is not already associated with a value (or is mapped to null), and enters it into this map unless null.
V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) It is used to compute a new mapping given the key and its current mapped value if the value for the specified key is present and non-null.
boolean containsValue(Object value) This method returns true if some value equal to the value exists within the map, else return false.
boolean containsKey(Object key) This method returns true if some key equal to the key exists within the map, else return false.
boolean equals(Object o) It is used to compare the specified Object with the Map.
void forEach(BiConsumer<? super K,? super V> action) It performs the given action for each entry in the map until all entries have been processed or the action throws an exception.
V get(Object key) This method returns the object that contains the value associated with the key.
V getOrDefault(Object key, V defaultValue) It returns the value to which the specified key is mapped, or defaultValue if the map contains no mapping for the key.
int hashCode() It returns the hash code value for the Map
boolean isEmpty() This method returns true if the map is empty; returns false if it contains at least one key.
V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction) If the specified key is not already associated with a value or is associated with null, associates it with the given non-null value.
V replace(K key, V value) It replaces the specified value for a specified key.
boolean replace(K key, V oldValue, V newValue) It replaces the old value with the new value for a specified key.
void replaceAll(BiFunction<? super K,? super V,? extends V> function) It replaces each entry's value with the result of invoking the given function on that entry until all entries have been processed or the function throws an exception.
Collectionvalues() It returns a collection view of the values contained in the map.
int size() This method returns the number of entries in the map.

Map.Entry Interface

An inner interface called Map.Entry is present within the Map interface. This interface represents a singular key-value pair within a map. It offers functionality to access the map, iterate through its entries, and transform the map into a set of keys and values. This interface returns a collection view of the map, with elements belonging to this particular class. It equips users with methods to retrieve both keys and values from the map.

Methods of Map.Entry interface

Method Description
K getKey() It is used to obtain a key.
V getValue() It is used to obtain value.
int hashCode() It is used to obtain hashCode.
V setValue(V value) It is used to replace the value corresponding to this entry with the specified value.
boolean equals(Object o) It is used to compare the specified object with the other existing objects.
static static <V>, Comparator<V> comparingByKey(),V> ComparatorComparator<V>> comparingByKey() It returns a comparator that compare the objects in natural order on key.
static static <V> Comparator<V> comparingByKey(Comparator<V> cmp) It returns a comparator that compare the objects by key using the given Comparator. ComparatorComparator<V>> comparingByKey(ComparatorComparator<V> cmp) It returns a comparator that compare the objects by key using the given Comparator.
static static <V> Comparator<V> comparingByValue()> ComparatorComparator<V>> comparingByValue() It returns a comparator that compare the objects in natural order on value.
static <V> ComparatorComparator<V>> comparingByValue(ComparatorComparator<V> cmp) It returns a comparator that compare the objects by value using the given Comparator.

Java Map Example: Non-Generic (Old Style)

Example

Example

//Non-generic

import java.util.*;

public class Main {

public static void main(String[] args) {

	Map map=new HashMap();

	//Adding elements to map

	map.put(1,"Amit");

	map.put(5,"Rahul");

	map.put(2,"Jai");

	map.put(6,"Amit");

	//Traversing Map

	Set set=map.entrySet();//Converting to Set so that we can traverse

	Iterator itr=set.iterator();

	while(itr.hasNext()){

		//Converting to Map.Entry so that we can get key and value separately

		Map.Entry entry=(Map.Entry)itr.next();

		System.out.println(entry.getKey()+" "+entry.getValue());

	}

}

}

Output:

Output

1 Amit

2 Jai

5 Rahul

6 Amit

Example

Example

import java.util.*;

class Main{

   public static void main(String args[]){

      // Creating a HashMap instance

      Map<Integer,String> map=new HashMap<Integer,String>();   

      // Adding key-value pairs to the map

      map.put(100,"Amit");

      map.put(101,"Vijay");

      map.put(102,"Rahul");  

      // Elements can traverse in any order

      // Iterating over the entries of the map

      for(Map.Entry m:map.entrySet()){

         // Printing the key-value pairs

         System.out.println(m.getKey()+" "+m.getValue());

      }

   }

}

Output:

Output

100 Amit

101 Vijay

102 Rahul

Java Map Example: comparingByKey

Example

Example

import java.util.*;

class Main {

    public static void main(String args[]) {

        // Creating a HashMap instance to store key-value pairs

        Map<Integer, String> map = new HashMap<Integer, String>();

        // Adding key-value pairs to the map

        map.put(100, "Amit");

        map.put(101, "Vijay");

        map.put(102, "Rahul");

        // Returns a Set view of the mappings contained in this map

        map.entrySet() // Returns a Set view of the mappings contained in this map

            .stream() // Returns a sequential Stream with this collection as its source

            .sorted(Map.Entry.comparingByKey()) // Sorted according to the provided Comparator

            .forEach(System.out::println); // Performs an action for each element of this stream

    }

}

Output:

Output

100=Amit

101=Vijay

102=Rahul

Java Map Example: comparingByKey in Descending Order

Example

Example

import java.util.*;

class main {

    public static void main(String args[]) {

        // Creating a HashMap instance to store key-value pairs

        Map<Integer, String> map = new HashMap<Integer, String>();

        // Adding key-value pairs to the map

        map.put(100, "Amit");

        map.put(101, "Vijay");

        map.put(102, "Rahul");

        // Returns a Set view of the mappings contained in this map

        map.entrySet() // Returns a Set view of the mappings contained in this map

            .stream() // Returns a sequential Stream with this collection as its source

            .sorted(Map.Entry.comparingByKey(Comparator.reverseOrder())) // Sorted according to the provided Comparator

            .forEach(System.out::println); // Performs an action for each element of this stream

    }

}

Output:

Output

102=Rahul

101=Vijay

100=Amit

Several classes implement the Map interface, with some of the frequently utilized ones being:

HashMap

A crucial data structure in Java is the HashMap, which serves as an implementation of the Map interface. It facilitates the storage of key-value pairs, ensuring that each key within it is unique and associated with only one value. This characteristic allows for efficient retrieval of values based on their corresponding keys.

An essential characteristic of HashMap is its foundation as a hash table, enabling efficient performance for fundamental tasks like adding, removing, and accessing data with constant time complexity on average, as long as a suitable hash function and capacity management are in place.

Example:

Example

Example

import java.util.HashMap;

import java.util.Map;

public class Main {

    public static void main(String[] args) {

        // Creating a HashMap to store student IDs and their corresponding names

        Map<Integer, String> studentMap = new HashMap<>();

        // Adding some student records to the HashMap

        studentMap.put(1001, "John Smith");

        studentMap.put(1002, "Emily Brown");

        studentMap.put(1003, "Michael Johnson");

        // Retrieving and printing a student's name based on their ID

        int studentIdToFind = 1002;

        String studentName = studentMap.get(studentIdToFind);

        if (studentName != null) {

            System.out.println("Student with ID " + studentIdToFind + " is: " + studentName);

        } else {

            System.out.println("Student with ID " + studentIdToFind + " not found.");

        }

        // Iterating over the entries of the HashMap and printing each key-value pair

        System.out.println("Student Records:");

        for (Map.Entry<Integer, String> entry : studentMap.entrySet()) {

            System.out.println("ID: " + entry.getKey() + ", Name: " + entry.getValue());

        }

        // Checking if a student ID exists in the HashMap

        int idToCheck = 1004;

        boolean exists = studentMap.containsKey(idToCheck);

        System.out.println("Student with ID " + idToCheck + " exists in records: " + exists);

    }

}

Output:

Output

Student with ID 1002 is: Emily BrownStudent Records:

ID: 1001, Name: John Smith

ID: 1002, Name: Emily Brown

ID: 1003, Name: Michael Johnson

Student with ID 1004 exists in records: false

LinkedHashMap

The LinkedHashMap class in Java is an extension of HashMap and also implements the Map interface. It functions similarly to HashMap but ensures a consistent iteration sequence, meaning it retains the order in which keys were added to the map (insertion-order). This quality makes LinkedHashMap suitable for scenarios where maintaining the order of insertion is crucial.

One unique aspect of LinkedHashMap is its ability to maintain a doubly linked list of its entries. This feature allows you to iterate through the entries based on either the order of insertion or the order of access, which refers to the sequence in which entries were most recently accessed.

Example:

Example

Example

import java.util.LinkedHashMap;

import java.util.Map;

public class Main {

    public static void main(String[] args) {

        // Creating a LinkedHashMap to store student IDs and their corresponding names

        Map<Integer, String> studentMap = new LinkedHashMap<>();

        // Adding some student records to the LinkedHashMap

        studentMap.put(1001, "John Smith");

        studentMap.put(1002, "Emily Brown");

        studentMap.put(1003, "Michael Johnson");

        // Iterating over the entries of the LinkedHashMap and printing each key-value pair

        System.out.println("Student Records:");

        for (Map.Entry<Integer, String> entry : studentMap.entrySet()) {

            System.out.println("ID: " + entry.getKey() + ", Name: " + entry.getValue());

        }

    }

}

Output:

Output

Student Records:

ID: 1001, Name: John Smith

ID: 1002, Name: Emily Brown

ID: 1003, Name: Michael Johnson

TreeHashMap

In Java, a TreeMap is a type of map that maintains key-value pairs in a Red-Black tree format, ensuring that the elements are organized based on their keys, either through natural ordering or a custom comparator. This particular data structure guarantees operations such as insertion, deletion, and retrieval to have a time complexity of O(log n). By implementing the NavigableMap interface, TreeMap enables users to utilize navigation and range-related functionalities. Common applications of TreeMap involve maintaining dictionaries in sorted sequences, executing range-based searches, and managing data in an orderly fashion.

Example:

Example

Example

import java.util.*;

public class Main {

    public static void main(String[] args) {

        // Creating a TreeMap to store student IDs and their corresponding names

        TreeMap<Integer, String> studentMap = new TreeMap<>();

        // Adding some student records to the TreeMap

        studentMap.put(1003, "Michael Johnson");

        studentMap.put(1001, "John Smith");

        studentMap.put(1002, "Emily Brown");

        // Iterating over the entries of the TreeMap and printing each key-value pair

        System.out.println("Student Records:");

        for (Map.Entry<Integer, String> entry : studentMap.entrySet()) {

            System.out.println("ID: " + entry.getKey() + ", Name: " + entry.getValue());

        }

    }

}

Output:

Output

Student Records:

ID: 1001, Name: John Smith

ID: 1002, Name: Emily Brown

ID: 1003, Name: Michael Johnson

Advantages of Map Interface

Mapping Data: The Map interface facilitates the storage of data in key-value pairs, ensuring uniqueness for each key stored in the map. This arrangement enables swift data retrieval based on the keys provided.

Efficient Data Access: Maps offer rapid retrieval of values using keys. The efficiency of element retrieval, insertion, and deletion depends on the implementation and the chosen operation (for example, HashMap or TreeMap). These operations typically have average-case time complexities of O(1) or O(log n), varying based on the specific implementation used.

Effective Data Organization: Maps serve as highly efficient instruments for the organization and storage of data. Utilizing internal structures such as hash tables within HashMap or balanced trees within TreeMap ensures that data is arranged optimally to facilitate rapid retrieval and modification processes.

Variability in Key Varieties: The Map interface enables keys to be objects that have implemented the hashCode and equals methods. This flexibility allows for the utilization of custom objects as keys, enabling the creation of intricate structures and mappings.

Capability to Traverse Entries: Maps provide functionalities for traversing their entries, allowing users to handle both keys and values. This functionality facilitates actions like looping through all key-value pairs, performing calculations, or filtering entries depending on specific criteria.

Handling Null Values: Depending on how it is set up, maps may have the capability to accommodate null values, enabling the storage and retrieval of null as a legitimate value within the map. This feature can be beneficial in scenarios where null represents a valid or absent value.

Resizable Capability: Maps possess the ability to adjust their size dynamically based on the addition or deletion of items. This feature ensures that maps efficiently handle varying data volumes without incurring unnecessary memory usage or compromising performance.

Variety of Implementations Available: Java provides various implementations of the Map interface like HashMap, LinkedHashMap, and TreeMap, each with unique qualities and performance attributes. This variety allows you to select the implementation that best suits your specific requirements, whether it's speed, memory usage, or iteration sequence.

Disadvantages of Map Interface

Performance Variability: The choice of implementation, such as HashMap or TreeMap, significantly impacts the performance of map operations like insertion, retrieval, and deletion. For example, HashMap typically offers constant-time performance on average. However, in worst-case scenarios involving hash collisions, its performance may degrade to linear time.

Memory Usage: Maps consume additional memory compared to other data structures due to the necessity to store both keys and values. In addition, certain map implementations, such as TreeMap, may require extra memory to maintain data structures like balanced trees.

In certain map implementations like HashMap, there is no guarantee of maintaining a sorted order of elements. This can pose a challenge when iterating through elements in a specific sequence. Unlike LinkedHashMap, TreeMap arranges data elements by their keys, which may not align with the intended order.

Primary Restrictions: The Map interface sets boundaries on keys by mandating their uniqueness within the map and enforcing the presence of corresponding equals and hashCode functions. This constraint becomes significant when handling custom key types or instances where keys lack the necessary support for these methods.

Synchronization Overhead: Numerous map implementations do not come with built-in thread safety, which can result in concurrency issues when multiple threads access them concurrently. One approach is to employ external synchronization techniques for ensuring map synchronization. Nonetheless, this can introduce performance penalties and the risk of race conditions and deadlocks.

Overhead of Iteration: The process of traversing through the elements of a map can result in additional costs, particularly with larger maps, since all elements need to be accessed. While it is feasible to iterate through maps using specific implementations like HashMap and LinkedHashMap, TreeMap could be more costly in terms of iteration expenses because of its tree-based organization.

Java Map MCQ

  1. Which class does not implement the Map interface?
  • HashMap
  • TreeMap
  • LinkedHashMap
  • ArrayList

Explanation: ArrayList is a class that implements the List interface, not the Map interface. The other classes (HashMap, TreeMap, and LinkedHashMap) all implement the Map interface.

  1. What is the time complexity of accessing a value in a HashMap by key?
  • O(1)
  • O(log n)
  • O(n)
  • O(n log n)

Explanation: In a HashMap, accessing a value by key typically has a time complexity of O(1), assuming a good hash function and low hash collisions.

  1. Which Map implementation maintains the insertion order?
  • HashMap
  • TreeMap
  • LinkedHashMap
  • Hashtable

Explanation: LinkedHashMap maintains the order of elements based on their insertion order, whereas HashMap and Hashtable do not.

  1. How does TreeMap store its elements?
  • Insertion order
  • Based on hash values
  • Sorted according to natural ordering or a comparator
  • No specific order

Explanation: TreeMap stores its elements sorted according to their natural ordering or by a comparator provided at map creation time.

  1. Which method is used to retrieve a value associated with a given key from a Map?
  • getValue
  • getKey
  • get
  • find

The get function is utilized to fetch the value linked with a particular key within a Map.

Input Required

This code uses input(). Please provide values below: