
Sort HashMap by Value in Java – Example Methods
Sorting a HashMap by its values is a common task in Java programming that often stumps developers who expect key-based operations. Unlike TreeMap which maintains natural ordering by keys, HashMap stores entries in an unpredictable order, making value-based sorting a multi-step process. This guide walks through several proven methods to sort HashMap entries by their values, covering both ascending and descending order scenarios, performance considerations, and real-world applications you’ll encounter in production systems.
How HashMap Value Sorting Works
HashMap sorting by value requires converting the key-value pairs into a sortable structure since HashMap itself doesn’t maintain any ordering. The process typically involves extracting entries, applying a comparator based on values, and collecting results into a new data structure.
The fundamental challenge stems from HashMap’s internal structure – it uses hash buckets for O(1) access time but sacrifices ordering. When you need value-based sorting, you’re essentially trading that fast access for ordered results.
Method 1: Using Streams and Collectors (Java 8+)
The most elegant approach leverages Java 8’s Stream API with custom collectors:
import java.util.*;
import java.util.stream.Collectors;
public class HashMapSorting {
public static void main(String[] args) {
HashMap<String, Integer> originalMap = new HashMap<>();
originalMap.put("apple", 50);
originalMap.put("banana", 20);
originalMap.put("cherry", 80);
originalMap.put("date", 10);
// Sort by value in ascending order
LinkedHashMap<String, Integer> sortedAsc = originalMap.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
// Sort by value in descending order
LinkedHashMap<String, Integer> sortedDesc = originalMap.entrySet()
.stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
System.out.println("Original: " + originalMap);
System.out.println("Ascending: " + sortedAsc);
System.out.println("Descending: " + sortedDesc);
}
}
This approach creates a new LinkedHashMap that preserves insertion order, which becomes the sorted order after processing. The key insight is using `LinkedHashMap::new` as the map supplier to maintain ordering.
Method 2: Traditional List-Based Sorting
For environments where Java 8+ features aren’t available or when you need more control over the sorting process:
import java.util.*;
public class TraditionalSorting {
public static LinkedHashMap<String, Integer> sortByValue(HashMap<String, Integer> map, boolean ascending) {
List<Map.Entry<String, Integer>> entries = new ArrayList<>(map.entrySet());
Collections.sort(entries, new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
if (ascending) {
return o1.getValue().compareTo(o2.getValue());
} else {
return o2.getValue().compareTo(o1.getValue());
}
}
});
LinkedHashMap<String, Integer> result = new LinkedHashMap<>();
for (Map.Entry<String, Integer> entry : entries) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
public static void main(String[] args) {
HashMap<String, Integer> scores = new HashMap<>();
scores.put("Alice", 95);
scores.put("Bob", 87);
scores.put("Charlie", 92);
scores.put("Diana", 98);
LinkedHashMap<String, Integer> topScores = sortByValue(scores, false);
System.out.println("Leaderboard: " + topScores);
}
}
Method 3: Custom Comparator with Generic Support
A reusable utility method that handles different value types:
import java.util.*;
import java.util.stream.Collectors;
public class GenericHashMapSorter {
public static <K, V extends Comparable<? super V>> LinkedHashMap<K, V>
sortByValue(Map<K, V> map, boolean ascending) {
Comparator<Map.Entry<K, V>> comparator = ascending ?
Map.Entry.comparingByValue() :
Map.Entry.<K, V>comparingByValue().reversed();
return map.entrySet()
.stream()
.sorted(comparator)
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
}
public static void main(String[] args) {
// Example with Double values
HashMap<String, Double> temperatures = new HashMap<>();
temperatures.put("New York", 15.5);
temperatures.put("Miami", 28.3);
temperatures.put("Denver", 8.1);
temperatures.put("Seattle", 12.7);
LinkedHashMap<String, Double> coldestFirst = sortByValue(temperatures, true);
System.out.println("Coldest to warmest: " + coldestFirst);
// Example with String values
HashMap<String, String> countries = new HashMap<>();
countries.put("US", "United States");
countries.put("UK", "United Kingdom");
countries.put("DE", "Germany");
countries.put("FR", "France");
LinkedHashMap<String, String> alphabetical = sortByValue(countries, true);
System.out.println("Alphabetical by name: " + alphabetical);
}
}
Performance Comparison and Benchmarks
Method | Time Complexity | Space Complexity | Best For | Java Version |
---|---|---|---|---|
Stream API | O(n log n) | O(n) | Readable, functional style | 8+ |
Collections.sort() | O(n log n) | O(n) | Older Java versions | 1.2+ |
TreeMap conversion | O(n log n) | O(n) | Maintaining sorted state | 1.2+ |
Priority Queue | O(n log k) | O(k) | Top-K scenarios | 1.5+ |
Here’s a benchmark test showing performance differences:
import java.util.*;
import java.util.stream.Collectors;
public class SortingBenchmark {
private static final int MAP_SIZE = 100000;
public static void main(String[] args) {
HashMap<String, Integer> testMap = generateTestData(MAP_SIZE);
// Warm up JVM
for (int i = 0; i < 5; i++) {
sortWithStreams(new HashMap<>(testMap));
sortWithCollections(new HashMap<>(testMap));
}
// Benchmark Stream API
long startTime = System.nanoTime();
for (int i = 0; i < 10; i++) {
sortWithStreams(new HashMap<>(testMap));
}
long streamTime = (System.nanoTime() - startTime) / 10;
// Benchmark Collections.sort
startTime = System.nanoTime();
for (int i = 0; i < 10; i++) {
sortWithCollections(new HashMap<>(testMap));
}
long collectionsTime = (System.nanoTime() - startTime) / 10;
System.out.printf("Stream API: %d ns%n", streamTime);
System.out.printf("Collections.sort: %d ns%n", collectionsTime);
System.out.printf("Performance ratio: %.2f%n", (double) streamTime / collectionsTime);
}
private static HashMap<String, Integer> generateTestData(int size) {
HashMap<String, Integer> map = new HashMap<>();
Random random = new Random(42); // Fixed seed for consistent results
for (int i = 0; i < size; i++) {
map.put("key" + i, random.nextInt(10000));
}
return map;
}
private static LinkedHashMap<String, Integer> sortWithStreams(HashMap<String, Integer> map) {
return map.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
}
private static LinkedHashMap<String, Integer> sortWithCollections(HashMap<String, Integer> map) {
List<Map.Entry<String, Integer>> entries = new ArrayList<>(map.entrySet());
Collections.sort(entries, Map.Entry.comparingByValue());
LinkedHashMap<String, Integer> result = new LinkedHashMap<>();
for (Map.Entry<String, Integer> entry : entries) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
}
Real-World Use Cases and Examples
Web Server Request Monitoring
When running applications on VPS services, monitoring request frequencies becomes crucial:
import java.util.*;
import java.util.stream.Collectors;
public class RequestMonitor {
private HashMap<String, Integer> requestCounts = new HashMap<>();
public void logRequest(String endpoint) {
requestCounts.merge(endpoint, 1, Integer::sum);
}
public LinkedHashMap<String, Integer> getTopEndpoints(int limit) {
return requestCounts.entrySet()
.stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.limit(limit)
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
}
public static void main(String[] args) {
RequestMonitor monitor = new RequestMonitor();
// Simulate request logging
String[] endpoints = {"/api/users", "/api/orders", "/api/products", "/health", "/api/users"};
int[] frequencies = {150, 89, 234, 12, 175};
for (int i = 0; i < endpoints.length; i++) {
for (int j = 0; j < frequencies[i]; j++) {
monitor.logRequest(endpoints[i]);
}
}
System.out.println("Top 3 endpoints:");
monitor.getTopEndpoints(3).forEach((endpoint, count) ->
System.out.printf("%s: %d requests%n", endpoint, count));
}
}
Database Query Performance Analysis
Perfect for analyzing slow queries on dedicated servers:
public class QueryPerformanceAnalyzer {
private HashMap<String, Double> queryTimes = new HashMap<>();
public void recordQuery(String query, double executionTimeMs) {
queryTimes.put(query, executionTimeMs);
}
public void printSlowestQueries() {
LinkedHashMap<String, Double> slowestFirst = queryTimes.entrySet()
.stream()
.sorted(Map.Entry.<String, Double>comparingByValue().reversed())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
System.out.println("Queries needing optimization:");
slowestFirst.entrySet().stream()
.filter(entry -> entry.getValue() > 100.0) // Queries over 100ms
.forEach(entry ->
System.out.printf("%.2fms: %s%n", entry.getValue(),
entry.getKey().length() > 50 ?
entry.getKey().substring(0, 50) + "..." : entry.getKey()));
}
}
Alternative Approaches and When to Use Them
TreeMap for Maintained Sorting
When you need persistent sorting throughout the application lifecycle:
import java.util.*;
public class MaintainedSorting {
// Custom comparator that sorts by value
private static class ValueComparator implements Comparator<String> {
private final Map<String, Integer> base;
public ValueComparator(Map<String, Integer> base) {
this.base = base;
}
@Override
public int compare(String a, String b) {
int valueComparison = base.get(a).compareTo(base.get(b));
return valueComparison != 0 ? valueComparison : a.compareTo(b);
}
}
public static TreeMap<String, Integer> createSortedByValue(Map<String, Integer> original) {
ValueComparator comparator = new ValueComparator(original);
TreeMap<String, Integer> sorted = new TreeMap<>(comparator);
sorted.putAll(original);
return sorted;
}
}
Priority Queue for Top-K Problems
When you only need the top N elements, Priority Queue offers better performance:
import java.util.*;
public class TopKElements {
public static <K, V extends Comparable<V>> List<Map.Entry<K, V>>
getTopK(Map<K, V> map, int k) {
PriorityQueue<Map.Entry<K, V>> minHeap = new PriorityQueue<>(
k, Map.Entry.comparingByValue()
);
for (Map.Entry<K, V> entry : map.entrySet()) {
if (minHeap.size() < k) {
minHeap.offer(entry);
} else if (entry.getValue().compareTo(minHeap.peek().getValue()) > 0) {
minHeap.poll();
minHeap.offer(entry);
}
}
List<Map.Entry<K, V>> result = new ArrayList<>(minHeap);
result.sort(Map.Entry.<K, V>comparingByValue().reversed());
return result;
}
}
Common Pitfalls and Best Practices
Memory Considerations
- Always consider memory overhead when creating sorted copies of large HashMaps
- For large datasets, consider streaming processing instead of loading everything into memory
- Use primitive collections (like TIntObjectHashMap from Trove) for better memory efficiency with primitive values
- Monitor garbage collection impact when frequently sorting large maps
Thread Safety Issues
// Wrong - not thread-safe
public class UnsafeSorting {
private HashMap<String, Integer> data = new HashMap<>();
public LinkedHashMap<String, Integer> getSortedData() {
// Race condition: data might be modified during sorting
return data.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(/*...*/));
}
}
// Correct - thread-safe approach
public class SafeSorting {
private final ConcurrentHashMap<String, Integer> data = new ConcurrentHashMap<>();
public LinkedHashMap<String, Integer> getSortedData() {
// Create snapshot for consistent sorting
Map<String, Integer> snapshot = new HashMap<>(data);
return snapshot.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
}
}
Null Value Handling
public class NullSafeComparator {
public static <K, V extends Comparable<V>> LinkedHashMap<K, V>
sortByValueNullSafe(Map<K, V> map, boolean nullsFirst) {
Comparator<Map.Entry<K, V>> comparator = nullsFirst ?
Map.Entry.<K, V>comparingByValue(Comparator.nullsFirst(Comparator.naturalOrder())) :
Map.Entry.<K, V>comparingByValue(Comparator.nullsLast(Comparator.naturalOrder()));
return map.entrySet().stream()
.sorted(comparator)
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
}
}
Integration with Popular Frameworks
Spring Boot Integration
Creating a reusable service for HashMap sorting in Spring applications:
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class MapSortingService {
public <K, V extends Comparable<? super V>> LinkedHashMap<K, V>
sortByValue(Map<K, V> map, SortOrder order) {
Comparator<Map.Entry<K, V>> comparator = order == SortOrder.ASC ?
Map.Entry.comparingByValue() :
Map.Entry.<K, V>comparingByValue().reversed();
return map.entrySet().stream()
.sorted(comparator)
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
}
public enum SortOrder {
ASC, DESC
}
}
Performance Monitoring
For production environments, especially on managed infrastructure, monitoring sorting performance becomes crucial:
import java.util.concurrent.atomic.AtomicLong;
public class MonitoredHashMapSorter {
private final AtomicLong sortOperations = new AtomicLong(0);
private final AtomicLong totalSortTime = new AtomicLong(0);
public <K, V extends Comparable<? super V>> LinkedHashMap<K, V>
sortByValueWithMetrics(Map<K, V> map) {
long startTime = System.nanoTime();
LinkedHashMap<K, V> result = map.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
long endTime = System.nanoTime();
sortOperations.incrementAndGet();
totalSortTime.addAndGet(endTime - startTime);
return result;
}
public double getAverageSortTimeMs() {
long ops = sortOperations.get();
return ops == 0 ? 0 : (totalSortTime.get() / ops) / 1_000_000.0;
}
}
The techniques covered here provide robust solutions for HashMap value sorting across different Java versions and use cases. Whether you’re building monitoring dashboards, analyzing performance metrics, or implementing leaderboard systems, these methods offer the flexibility and performance needed for production applications. For detailed Java Collections documentation, refer to Oracle’s official Collections guide.

This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.
This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.