This tutorial explains the usage of Maps and HashMap with Java.
1. Using Maps
1.1. Map and HashMap
The Map
interface defines an object that maps keys to values.
A map cannot contain duplicate keys; each key can map to at most one value.
The HashMap
class is an efficient implementation of the Map
interface.
1.2. Example Program
To see the following code snippets in action, insert them into the main method where put test code here is stated.
package com.vogella.java.collections.map;
import java.util.HashMap;
import java.util.Map;
public class MapTester {
public static void main(String[] args) {
// put test code here
}
}
1.3. Initialize a HashMap in Java
The following code demonstrates how to initialize a HashMap
in Java.
1.3.1. Initialize an Empty Map
In the following code, an empty map with keys and values of type String
is initialized.
Map<String, String> map = Map.of();
1.3.2. Initialize an Unmodifiable Map with Map.of for Small Maps
In the following example, a map with several entries is initialized. This factory method supports a maximum of 10 key-value pairs and requires at least Java 9.
Map<String, String> map = Map.of("key1", "value1", "key2", "value2");
1.3.3. Initialize an Unmodifiable Map via the Map.ofEntries()
Map<String, String> map = Map.ofEntries(
Map.entry("key1", "value1"),
Map.entry("key2", "value2"),
// more entries can be added here
Map.entry("key100", "value100")
);
1.3.4. Initialize a Map via the New Operator
The following code creates a map using the new
operator and adds multiple entries to it via the put
method.
Map<String, String> map = new HashMap<>();
map.put("Android", "Mobile");
map.put("Eclipse IDE", "Java");
map.put("Eclipse RCP", "Java");
map.put("Git", "Version control system");
1.4. Remove an Entry from a Map
You can remove an entry from a map using the remove
method.
Map<String, String> map = Map.of("Android", "Mobile OS", "Flutter", "Development environment");
map.remove("Android");
1.5. Process the Map
To process every element in a map, you can use the forEach
method, which takes a lambda as a parameter.
map.forEach((k, v) -> System.out.printf("%s %s%n", k, v));
1.6. Convert Keys in a Map to an Array or a List
You can convert the keys or values to an array or a list. The following code demonstrates this process.
Map<String, String> map = new HashMap<>();
map.put("Android", "Mobile");
map.put("Eclipse IDE", "Java");
map.put("Eclipse RCP", "Java");
map.put("Git", "Version control system");
// Convert keys to array
String[] keys = map.keySet().toArray(new String[0]);
for (String key : keys) {
System.out.println(key);
}
// Convert keys to list
List<String> list = new ArrayList<>(map.keySet());
for (String key : list) {
System.out.println(key);
}
1.7. Getting the Current Value or a Default for a Map
You can use the getOrDefault()
method to return the value for a key or a default value if the key is not present in the map.
Map<String, Integer> map = new HashMap<>();
map.put("Android", 1 + map.getOrDefault("Android", 0));
// Write to command line
map.forEach((k, v) -> System.out.printf("%s %s%n", k, v));
1.8. Compute If Absent
The computeIfAbsent
method calculates and adds an entry to the map if it is not already present.
Map<String, Integer> map = new HashMap<>();
Integer calculatedValue = map.computeIfAbsent("Java", it -> 0);
System.out.println(calculatedValue);
map.keySet().forEach(key -> System.out.println(key + " " + map.get(key)));
2. Sorting Maps in Java
Sorting maps in Java can be achieved in several ways, depending on the requirements for ordering keys or values. This chapter explores the methods to sort maps, focusing on the TreeMap
for key sorting and using streams for sorting by values.
2.1. Sorting by Keys
The simplest way to sort a map by keys is to use TreeMap
, which maintains the natural ordering of its keys or can be constructed with a custom comparator.
2.1.1. Using TreeMap
A TreeMap
automatically sorts the keys in their natural order or according to a specified comparator.
import java.util.Map;
import java.util.TreeMap;
public class SortMapByKeys {
public static void main(String[] args) {
Map<String, Integer> map = new TreeMap<>();
map.put("Banana", 3);
map.put("Apple", 5);
map.put("Orange", 2);
// Display the sorted map
map.forEach((k, v) -> System.out.println(k + ": " + v));
}
}
2.2. Sorting by Values
To sort a map by its values, you can use Java Streams to create a sorted representation of the entries.
2.2.1. Example of Sorting by Values
Here’s how to sort a HashMap
by values using streams:
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class SortMapByValues {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("Banana", 3);
map.put("Apple", 5);
map.put("Orange", 2);
// Sort by values
Map<String, Integer> sortedByValues = map.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new // Maintain insertion order
));
// Display the sorted map
sortedByValues.forEach((k, v) -> System.out.println(k + ": " + v));
}
}
2.3. Custom Sorting
You can also implement custom sorting logic using comparators. For instance, to sort a map by values in descending order:
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class CustomSortMapByValues {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("Banana", 3);
map.put("Apple", 5);
map.put("Orange", 2);
// Sort by values in descending order
Map<String, Integer> sortedByValuesDescending = map.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue((v1, v2) -> v2.compareTo(v1))) // Custom comparator
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new // Maintain insertion order
));
// Display the sorted map
sortedByValuesDescending.forEach((k, v) -> System.out.println(k + ": " + v));
}
}
2.4. Conclusion
Sorting maps can be easily accomplished using TreeMap
for key-based sorting or Java Streams for value-based sorting.
By employing custom comparators, you can tailor the sorting behavior to suit your application’s requirements.
Understanding these methods enhances your ability to manipulate and present data effectively in Java.
3. Using ConcurrentHashMap
In multi-threaded environments, handling data shared among threads can lead to synchronization issues. The ConcurrentHashMap
class is designed to allow concurrent access without locking the entire map, making it a suitable choice for high-performance applications.
3.1. Overview of ConcurrentHashMap
The ConcurrentHashMap
is part of the Java Collections Framework and provides an efficient way to manage key-value pairs in a multi-threaded context. Unlike regular maps, ConcurrentHashMap
allows multiple threads to read and write to the map concurrently without causing data inconsistencies.
3.2. Creating a ConcurrentHashMap
You can create a ConcurrentHashMap
similarly to other map implementations. Here’s an example:
import java.util.concurrent.ConcurrentHashMap;
ConcurrentHashMap<String, String> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("key1", "value1");
concurrentMap.put("key2", "value2");
3.3. Key Features of ConcurrentHashMap
-
Concurrency Level:
-
ConcurrentHashMap
allows you to specify a concurrency level during initialization, which defines how many threads can access the map concurrently.
-
-
Non-blocking Reads:
-
Read operations (e.g.,
get
) do not block other threads, making them very efficient.
-
-
Segmented Locks:
-
Instead of locking the entire map,
ConcurrentHashMap
divides the map into segments. Each segment can be locked independently, allowing better concurrent performance.
-
-
Atomic Operations:
-
The map provides atomic methods like
putIfAbsent
,remove
, andreplace
, ensuring thread safety for these operations.
-
3.4. Example Usage of ConcurrentHashMap
Here’s a simple example demonstrating the use of ConcurrentHashMap
in a multi-threaded application:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentMapExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
// Create and start multiple threads that modify the map
Runnable task = () -> {
for (int i = 0; i < 5; i++) {
concurrentMap.put(Thread.currentThread().getName() + i, i);
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
// Wait for threads to finish
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// Display the contents of the map
concurrentMap.forEach((k, v) -> System.out.println(k + ": " + v));
}
}
3.5. Synchronization Issues
When using ConcurrentHashMap
, it is essential to understand that:
-
Concurrent Modifications: Multiple threads can modify the map at the same time, but if your logic depends on checking a value before updating it, you may need to use atomic operations or additional synchronization.
-
Iterating Over the Map: If you need to iterate over the map while it may be modified by other threads, use the
forEach
method orkeySet
in a way that doesn’t assume a consistent view of the map.
3.6. Best Practices
-
Use Atomic Operations:
-
Leverage atomic methods provided by
ConcurrentHashMap
to perform updates safely without explicit locking.
-
-
Avoid External Synchronization:
-
Since
ConcurrentHashMap
handles its own synchronization, avoid wrapping it with synchronized blocks unless absolutely necessary.
-
-
Profile Your Application:
-
Always profile and test your application under concurrent loads to ensure the map performs as expected.
-
By following these guidelines and understanding the capabilities of ConcurrentHashMap
, you can effectively manage shared data in multi-threaded applications without compromising performance or data integrity.
== Common Use Cases for Maps in Java
Maps are versatile data structures that provide efficient key-value pair storage. This chapter explores several common use cases for maps in Java, illustrating their practicality in various scenarios.
3.7. 1. Frequency Counting
One of the most common use cases for maps is counting the occurrences of items, such as words in a text or characters in a string. This can be efficiently implemented using a HashMap
.
3.7.1. Example: Counting Word Frequencies
import java.util.HashMap;
import java.util.Map;
public class WordFrequencyCounter {
public static void main(String[] args) {
String text = "apple banana apple orange banana apple";
Map<String, Integer> frequencyMap = new HashMap<>();
for (String word : text.split(" ")) {
frequencyMap.put(word, frequencyMap.getOrDefault(word, 0) + 1);
}
frequencyMap.forEach((k, v) -> System.out.println(k + ": " + v));
}
}
3.8. 2. Caching Data
Maps are often used for caching data to improve performance in applications. By storing previously retrieved results in a map, you can avoid expensive computations or database calls.
3.8.1. Example: Simple Cache Implementation
import java.util.HashMap;
import java.util.Map;
public class SimpleCache {
private Map<String, String> cache = new HashMap<>();
public String getData(String key) {
// Check if the data is already in the cache
return cache.getOrDefault(key, fetchData(key));
}
private String fetchData(String key) {
// Simulate data fetching (e.g., from a database)
String data = "Data for " + key;
cache.put(key, data);
return data;
}
public static void main(String[] args) {
SimpleCache cache = new SimpleCache();
System.out.println(cache.getData("item1")); // Fetches data
System.out.println(cache.getData("item1")); // Returns cached data
}
}
3.9. 3. Configuration Settings
Maps can efficiently store configuration settings for applications. You can easily retrieve and modify settings based on keys.
3.9.1. Example: Application Configuration
import java.util.HashMap;
import java.util.Map;
public class AppConfig {
private Map<String, String> config = new HashMap<>();
public AppConfig() {
// Initialize configuration settings
config.put("url", "https://example.com");
config.put("timeout", "30");
}
public String getConfig(String key) {
return config.get(key);
}
public static void main(String[] args) {
AppConfig appConfig = new AppConfig();
System.out.println("URL: " + appConfig.getConfig("url"));
System.out.println("Timeout: " + appConfig.getConfig("timeout"));
}
}
3.10. 4. Grouping Data
Maps can be used to group related data together, such as associating a list of values with a single key.
3.10.1. Example: Grouping Students by Class
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GroupStudents {
public static void main(String[] args) {
Map<String, List<String>> classMap = new HashMap<>();
// Adding students to classes
classMap.computeIfAbsent("Class A", k -> new ArrayList<>()).add("Alice");
classMap.computeIfAbsent("Class A", k -> new ArrayList<>()).add("Bob");
classMap.computeIfAbsent("Class B", k -> new ArrayList<>()).add("Charlie");
// Displaying students in each class
classMap.forEach((k, v) -> System.out.println(k + ": " + v));
}
}
3.11. 5. Storing Relationships
Maps can represent relationships between entities, such as users and their friends or products and their categories.
3.11.1. Example: User Friendships
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class UserFriendships {
private Map<String, Set<String>> friendships = new HashMap<>();
public void addFriend(String user, String friend) {
friendships.computeIfAbsent(user, k -> new HashSet<>()).add(friend);
friendships.computeIfAbsent(friend, k -> new HashSet<>()).add(user); // Bidirectional friendship
}
public Set<String> getFriends(String user) {
return friendships.getOrDefault(user, new HashSet<>());
}
public static void main(String[] args) {
UserFriendships uf = new UserFriendships();
uf.addFriend("Alice", "Bob");
uf.addFriend("Alice", "Charlie");
System.out.println("Alice's friends: " + uf.getFriends("Alice"));
}
}
3.12. Conclusion
Maps provide a powerful and flexible way to manage data in Java. By understanding these common use cases, you can leverage maps to solve various programming challenges efficiently and effectively.
If you need more assistance we offer Online Training and Onsite training as well as consulting