In this lesson, we will explore the concept and practical application of HashMaps in Java. HashMaps are a powerful and efficient data structure used for storing key-value pairs. You will learn how to utilize HashMap
to count the frequency of elements in a collection, understand the underlying mechanics, and analyze the time and space efficiency of this approach. This lesson includes a step-by-step demonstration with detailed code examples and a discussion on the practical applications of using HashMaps for counting occurrences in various contexts.
We begin in a library, where we want to count book copies. With a small collection, we might be able to tally each one manually. However, as the collection grows, this approach becomes cumbersome and inefficient. A more efficient method uses a HashMap.
For a quick illustration, consider this list of colors:
Java1import java.util.ArrayList; 2 3class Solution { 4 public static void main(String[] args) { 5 ArrayList<String> colors = new ArrayList<>(); 6 colors.add("red"); 7 colors.add("blue"); 8 colors.add("red"); 9 colors.add("green"); 10 colors.add("blue"); 11 colors.add("blue"); 12 } 13}
If we count manually, red
appears twice, blue
appears thrice, and green
appears once. We can employ HashMaps
for a more efficient counting process.
Simple yet powerful, HashMaps
allow us to store and retrieve data using keys. The unique colors in our list act as keys, and the count of each color becomes its corresponding value. Let's demonstrate how we can count elements in our colors
list using Java's HashMap
:
Java1import java.util.HashMap; 2import java.util.ArrayList; 3 4class Solution { 5 public static void main(String[] args) { 6 ArrayList<String> colors = new ArrayList<>(); 7 colors.add("red"); 8 colors.add("blue"); 9 colors.add("red"); 10 colors.add("green"); 11 colors.add("blue"); 12 colors.add("blue"); 13 14 HashMap<String, Integer> colorMap = new HashMap<>(); 15 16 // Start the loop to iterate over each color 17 for (String color : colors) { 18 19 // If the color is present in our HashMap, increment its value by 1 20 if (colorMap.containsKey(color)) { 21 colorMap.put(color, colorMap.get(color) + 1); 22 } else { 23 // If the color isn't present, it means we're encountering this color in our list for the first time. 24 // In this case, we add it to our HashMap and set its value to 1 25 colorMap.put(color, 1); 26 } 27 } 28 29 // Print our HashMap with counts 30 for (String key : colorMap.keySet()) { 31 System.out.println(key + ": " + colorMap.get(key)); 32 } 33 } 34}
When the above code executes, it displays the counts for each color:
1red: 2 2green: 1 3blue: 3
Here's how we created a HashMap
to count our elements:
We began with an empty HashMap
. Then, we went through our list, and for every occurring element, we simply incremented its value in the HashMap
. If the element was not already in the HashMap
, it would be added with an initial value of 1
.
Unlike some languages, Java's HashMap
does not automatically assign a default value of 0
when a new key is encountered. Instead, we explicitly initialize the value to 1
when a new key is added. This makes our code different in handling non-existent keys.
Here, the optimized solution leverages the getOrDefault
method:
Java1import java.util.HashMap; 2import java.util.ArrayList; 3 4class Solution { 5 public static void main(String[] args) { 6 ArrayList<String> colors = new ArrayList<>(); 7 colors.add("red"); 8 colors.add("blue"); 9 colors.add("red"); 10 colors.add("green"); 11 colors.add("blue"); 12 colors.add("blue"); 13 14 HashMap<String, Integer> colorMap = new HashMap<>(); 15 16 // Iterate over each color and increase its count 17 for (String color : colors) { 18 colorMap.put(color, colorMap.getOrDefault(color, 0) + 1); 19 } 20 21 // Print our HashMap with counts 22 for (String key : colorMap.keySet()) { 23 System.out.println(key + ": " + colorMap.get(key)); 24 } 25 } 26}
The getOrDefault
method simplifies the code by directly handling default values. Here’s how it works:
colorMap.getOrDefault(color, 0)
checks if the color
is a key in the HashMap
.color
is present, colorMap.get(color)
retrieves its current count.color
is absent, getOrDefault
returns the default value 0
.This method eliminates the need for the if...else
structure, making the code cleaner and more concise, while still ensuring that the value is correctly incremented or initialized as needed. Thus, it efficiently counts the colors in our list, showcasing how efficient counting can be even as the list size increases!
The time complexity of our approach is O(n)
, where n
is the number of elements in our list. This is because we iterate over our list exactly once, performing constant-time operations for each element. Here is why:
HashMap
(both setting a value and getting a value) in Java are typically O(1)
, constant-time operations.for
loop iterates over each element in the list exactly once, so it is an O(n)
operation.The total time complexity, therefore, remains O(n)
because the time taken is directly proportional to the number of items in the list. As the size of the list increases, the time taken scales linearly, making this approach efficient for larger collections.
It is also worth noting that the space complexity of this approach is O(k)
, where k
is the number of unique elements in the list. In the worst-case scenario, where all elements are unique, the space complexity would be O(n)
.
In conclusion, using HashMaps
for counting is a time-efficient approach, especially when working with large datasets.
This approach can be applied to larger lists, strings, and nested collections to count elements. Counting is a ubiquitous task in areas like data analysis and natural language processing. You can employ this concept to count the frequency of words in sentences, characters in strings, or items in shopping lists.
Now, let's solidify the concept of counting occurrences using HashMaps with hands-on exercises. The core of this lesson has shown you how HashMaps
can be used for efficient element counting. They are beneficial for enhancing code performance and organization!