Java HashMap
Introduction
HashMap is one of the most frequently used implementations of the Map interface in the Java Collections Framework. It stores data in key-value pairs, providing quick access to values when the key is known. HashMap uses a hashing technique to store elements, making retrieval operations highly efficient with an average time complexity of O(1).
In this tutorial, we'll explore how HashMaps work, how to use them effectively, and some common use cases where HashMaps shine.
What is a HashMap?
HashMap is a class that implements the Map interface, which maps unique keys to values. It uses a technique called hashing to store the key-value pairs efficiently. Some key characteristics of HashMap include:
- It allows one null key and multiple null values
- It doesn't guarantee the order of elements (not synchronized)
- It doesn't maintain insertion order (use LinkedHashMap if you need this)
- It provides constant-time performance for basic operations (get and put)
HashMap vs Other Map Implementations
Creating a HashMap
Let's start by creating a simple HashMap:
// Import statement
import java.util.HashMap;
// Creating a HashMap with String keys and Integer values
HashMap<String, Integer> studentScores = new HashMap<>();
// Alternative way using the Map interface
Map<String, Integer> studentScores = new HashMap<>();
Basic Operations
Adding Elements
To add elements to a HashMap, use the put()
method:
// Creating a HashMap of student names and scores
HashMap<String, Integer> studentScores = new HashMap<>();
// Adding elements
studentScores.put("John", 85);
studentScores.put("Lisa", 92);
studentScores.put("Mike", 78);
studentScores.put("Sarah", 95);
System.out.println("Student scores: " + studentScores);
Output:
Student scores: {Sarah=95, Mike=78, John=85, Lisa=92}
Note: The order may be different when you run this code as HashMap doesn't maintain insertion order.
Accessing Elements
To retrieve a value by its key, use the get()
method:
// Get a specific student's score
Integer lisaScore = studentScores.get("Lisa");
System.out.println("Lisa's score: " + lisaScore);
// Trying to get a value for a key that doesn't exist
Integer alexScore = studentScores.get("Alex");
System.out.println("Alex's score: " + alexScore);
Output:
Lisa's score: 92
Alex's score: null
Checking if a Key Exists
Before accessing a value, you might want to check if a key exists:
// Check if a key exists
boolean hasLisa = studentScores.containsKey("Lisa");
System.out.println("Contains Lisa: " + hasLisa);
boolean hasAlex = studentScores.containsKey("Alex");
System.out.println("Contains Alex: " + hasAlex);
Output:
Contains Lisa: true
Contains Alex: false
Checking if a Value Exists
Similarly, you can check if a specific value exists:
// Check if a value exists
boolean hasScore92 = studentScores.containsValue(92);
System.out.println("Contains score 92: " + hasScore92);
boolean hasScore100 = studentScores.containsValue(100);
System.out.println("Contains score 100: " + hasScore100);
Output:
Contains score 92: true
Contains score 100: false
Removing Elements
To remove an entry from the HashMap:
// Remove a student
studentScores.remove("Mike");
System.out.println("After removing Mike: " + studentScores);
Output:
After removing Mike: {Sarah=95, John=85, Lisa=92}
Size and Empty Check
To check the size of a HashMap or if it's empty:
// Get the size
int size = studentScores.size();
System.out.println("Size of HashMap: " + size);
// Check if empty
boolean isEmpty = studentScores.isEmpty();
System.out.println("Is HashMap empty? " + isEmpty);
// Clear the HashMap
studentScores.clear();
System.out.println("After clearing: " + studentScores);
System.out.println("Is HashMap empty now? " + studentScores.isEmpty());
Output:
Size of HashMap: 3
Is HashMap empty? false
After clearing: {}
Is HashMap empty now? true
Iterating Over a HashMap
There are several ways to iterate through a HashMap:
Using entrySet()
HashMap<String, Integer> fruits = new HashMap<>();
fruits.put("Apple", 10);
fruits.put("Banana", 5);
fruits.put("Orange", 8);
// Iterate using entrySet
for (Map.Entry<String, Integer> entry : fruits.entrySet()) {
System.out.println("Fruit: " + entry.getKey() + ", Quantity: " + entry.getValue());
}
Output:
Fruit: Apple, Quantity: 10
Fruit: Orange, Quantity: 8
Fruit: Banana, Quantity: 5
Using keySet()
// Iterate over keys
for (String fruit : fruits.keySet()) {
System.out.println("Fruit: " + fruit);
}
Output:
Fruit: Apple
Fruit: Orange
Fruit: Banana
Using values()
// Iterate over values
for (Integer quantity : fruits.values()) {
System.out.println("Quantity: " + quantity);
}
Output:
Quantity: 10
Quantity: 8
Quantity: 5
Using forEach() (Java 8+)
// Using forEach method (Java 8+)
fruits.forEach((fruit, quantity) -> {
System.out.println(fruit + ": " + quantity);
});
Output:
Apple: 10
Orange: 8
Banana: 5
HashMap with Custom Objects
When using custom objects as keys in a HashMap, you must override the hashCode()
and equals()
methods to ensure proper functioning.
class Student {
private int id;
private String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Student student = (Student) obj;
return id == student.id && name.equals(student.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
@Override
public String toString() {
return "Student{id=" + id + ", name='" + name + "'}";
}
}
// Usage example
HashMap<Student, Integer> studentGrades = new HashMap<>();
studentGrades.put(new Student(1, "John"), 85);
studentGrades.put(new Student(2, "Lisa"), 92);
// This will work correctly due to properly implemented equals and hashCode
Student john = new Student(1, "John");
System.out.println("John's grade: " + studentGrades.get(john)); // Outputs: 85
Practical Examples
Example 1: Word Frequency Counter
A common use case for HashMaps is counting frequencies:
public class WordFrequencyCounter {
public static void main(String[] args) {
String text = "this is a sample text with some words that repeat and some words that don't repeat";
String[] words = text.toLowerCase().split(" ");
HashMap<String, Integer> wordFrequency = new HashMap<>();
// Count word frequencies
for (String word : words) {
// If the word is seen for the first time, put 1, otherwise increment the count
wordFrequency.put(word, wordFrequency.getOrDefault(word, 0) + 1);
}
// Display word frequencies
System.out.println("Word Frequencies:");
for (Map.Entry<String, Integer> entry : wordFrequency.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
Output:
Word Frequencies:
that: 2
words: 2
is: 1
with: 1
repeat: 2
a: 1
some: 2
and: 1
sample: 1
text: 1
this: 1
don't: 1
Example 2: Caching Computed Results
HashMaps are great for caching previously computed results:
public class FibonacciCache {
private HashMap<Integer, Long> cache = new HashMap<>();
public long fibonacci(int n) {
// Check if the result is already in the cache
if (cache.containsKey(n)) {
System.out.println("Using cached value for fib(" + n + ")");
return cache.get(n);
}
// Compute the value if not in cache
long result;
if (n <= 1) {
result = n;
} else {
result = fibonacci(n - 1) + fibonacci(n - 2);
}
// Store the result in the cache
cache.put(n, result);
return result;
}
public static void main(String[] args) {
FibonacciCache calculator = new FibonacciCache();
System.out.println("fib(30) = " + calculator.fibonacci(30));
System.out.println("fib(30) again = " + calculator.fibonacci(30)); // Uses cache
}
}
HashMap Performance Considerations
- Initial Capacity: You can specify the initial capacity to avoid rehashing operations, which can improve performance.
- Load Factor: Controls how full the HashMap can be before it's resized.
- Time Complexity: Average case O(1) for get and put operations, but worst-case scenario can be O(n).
// Creating HashMap with initial capacity and load factor
HashMap<String, Integer> optimizedMap = new HashMap<>(100, 0.75f);
Common Methods Reference
Method | Description |
---|---|
put(K key, V value) | Adds a key-value mapping to the map |
get(Object key) | Returns the value associated with the specified key |
remove(Object key) | Removes the mapping for the specified key |
containsKey(Object key) | Returns true if the map contains the specified key |
containsValue(Object value) | Returns true if the map maps one or more keys to the specified value |
size() | Returns the number of key-value mappings |
isEmpty() | Returns true if the map contains no key-value mappings |
clear() | Removes all mappings from the map |
keySet() | Returns a Set view of the keys contained in the map |
values() | Returns a Collection view of the values contained in the map |
entrySet() | Returns a Set view of the mappings contained in the map |
Summary
HashMap is a powerful data structure in Java that provides fast access to values using keys. It's ideal for scenarios where quick lookups are needed and the insertion order is not important. Some key points to remember:
- HashMaps store key-value pairs with unique keys
- They provide O(1) average time complexity for most operations
- They don't maintain the insertion order
- When using custom objects as keys, remember to override equals() and hashCode()
- HashMaps allow one null key and multiple null values
Exercises
- Create a HashMap to store and retrieve student information (name, age, grade).
- Write a program to find duplicate characters in a string using HashMap.
- Implement a simple cache system using HashMap to store results of expensive operations.
- Write a function that takes two arrays and returns elements that appear in both arrays using a HashMap.
- Create a program that uses a HashMap to count the occurrence of each word in a text file.
Additional Resources
- Java HashMap Documentation
- Java Collections Framework Overview
- Book: "Effective Java" by Joshua Bloch (Chapter on Collections)
- Book: "Java Generics and Collections" by Maurice Naftalin and Philip Wadler
Happy HashMap coding!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)