Explain Codes LogoExplain Codes Logo

Sort a Map by values

java
prompt-engineering
functions
collections
Alex KataevbyAlex Kataev·Sep 25, 2024
TLDR

Quickly convert an unsorted Map<Key, Value> into a sorted LinkedHashMap using the Java 8 Stream API:

Map<String, Integer> sortedMap = unsortedMap.entrySet() .stream() .sorted(Map.Entry.comparingByValue()) .collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new ));

Here, we first stream the entries, sort them by value, and then collect them in a LinkedHashMap, maintaining the order.

Advanced sorting scenarios

If the values are not Comparable or you need custom sorting rules, use a custom Comparator:

Comparator<Entry<Key, Value>> customComparator = (e1, e2) -> { // You're the boss here! Write your custom comparison logic. }; Map<Key, Value> sortedByCustom = unsortedMap.entrySet() .stream() .sorted(customComparator) .collect(Collectors.toMap( Entry::getKey, Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new ));

Want to turn the tables and sort in descending order? Bring in Collections.reverseOrder():

map.entrySet() .stream() .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) // Sort Descending, like an upside-down world! .forEachOrdered(e -> resultMap.put(e.getKey(), e.getValue()));

For a list of top 'n' entries, use the limit(n) method with your stream:

map.entrySet() .stream() .sorted(Map.Entry.comparingByValue()) .limit(n) // The "cream of the crop", so to speak. .forEachOrdered(e -> topNMap.put(e.getKey(), e.getValue()));

Dynamic map sorting

When the map changes and you need consistent sorting, use a ValueComparableMap or populate a TreeMap with a predefined comparator:

TreeMap<Key, Value> sortedMap = new TreeMap<>(new ValueComparator(unsortedMap)); sortedMap.putAll(unsortedMap); // Immutable sorted maps? Guava's got you covered: ImmutableSortedMap<Key, Value> immutableSortedMap = ImmutableSortedMap.copyOf(unsortedMap, customComparator);

Create a ValueComparator that can handle equal values and avoid losing keys:

class ValueComparator implements Comparator<Key> { Map<Key, Value> base; public ValueComparator(Map<Key, Value> base) { this.base = base; } // We compare by value, but if they tie, we compare by key, like a tiebreaker round! public int compare(Key a, Key b) { int valueComparison = base.get(a).compareTo(base.get(b)); return valueComparison != 0 ? valueComparison : a.compareTo(b); // Assuming Key is Comparable } }

Use the @SuppressWarnings("unchecked") annotation to suppress unchecked cast warnings with a smirk:

@SuppressWarnings("unchecked") // Don't worry JVM, I've got this! Comparator<Entry<Key, Value>> comparator = (Comparator<Entry<Key, Value>>) (Comparator<?>) Entry.comparingByValue();

For the Guava lovers out there:

Ordering<Key> ordering = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural()); // Guava magic!

Convert Sorted Set to Ordered Map

After sorting the entrySet, produce an ordered LinkedHashMap:

LinkedHashMap<Key, Value> sortedMap = new LinkedHashMap<>(); for (Entry<Key, Value> entry : entriesSortedByValues) { sortedMap.put(entry.getKey(), entry.getValue()); // One by one, like a disciplined line at a school cafetaria }

Additional use-cases and pitfalls

Streams with custom Comparators

Comparator<Entry<Key, Value>> customComparator = // Define what's best for your requirements map.entrySet().stream().sorted(customComparator) // Apply the "special sauce" .collect(Collectors.toMap( Entry::getKey, Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new ));

Managing duplicate values

While collecting, provide a merge function to handle equal value clash, or some keys may "get lost in the woods":

Map<Key, Value> sortedMap = unsortedMap.entrySet().stream() // Make sure everyone finds their way home! .sorted(Entry.comparingByValue()) .collect(...); // Merge function to handle key clashes

Attention to large collections

Working with large maps? Be aware of sorting performance. For better performance, use parallel streams: .parallelStream().

Potential hiccups

  • Key conflation: If multiple keys have the same value, you risk losing entries. Take care to handle this in your merge function.
  • Type safety: Make sure that your values are Comparable or that you have a safe custom Comparator up your sleeve.