How to sort a LinkedHashMap by value in decreasing order in java stream?

18,589

Solution 1

To sort in reverse order, pass Comparator.reverseOrder() as parameter to comparingByValue.

To get a LinkedHashMap, you must specifically request one with the 4-argument toMap(). If you don't specify what kind of a map you want, you will get whatever the default is, which currently happens to be a HashMap. Since HashMap doesn't preserve the order of elements, it will definitely not do for you.

myMap.entrySet().stream()
        .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
        .collect(Collectors.toMap(
                Map.Entry::getKey, 
                Map.Entry::getValue, 
                (x,y)-> {throw new AssertionError();},
                LinkedHashMap::new
        ));

With static imports, it becomes a bit more pleasant:

myMap.entrySet().stream()
        .sorted(comparingByValue(reverseOrder()))
        .collect(toMap(
                Map.Entry::getKey, 
                Map.Entry::getValue, 
                (x,y)-> {throw new AssertionError();},
                LinkedHashMap::new
        ));

Solution 2

You can pass whatever Comparator you wish to comparingByValue.

For example (I hope I got the syntax right, since I can't test it):

myMap.entrySet().stream()
    .sorted(Map.Entry.comparingByValue((v1,v2)->v2.compareTo(v1)))
    .collect(Collectors.toMap(Entry::getKey, Entry::getValue));

By comparing the values of the two entries in the opposite order, using the natural ordering (Comparable's compareTo), you get a reversed order compared to what comparingByValue() (which is equivalent to comparingByValue((v1,v2)->v1.compareTo(v2))) will give you.

BTW, I'm not sure that Collectors.toMap returns a LinkedHashMap instance, and even if it currently does, it can change in the future, since the Javadoc doesn't mention it, so you can't rely on it.

To make sure that the resulting Map would be a LinkedHashMap, you should use a different variant of toMap :

myMap.entrySet().stream()
    .sorted(Map.Entry.comparingByValue((v1,v2)->v2.compareTo(v1)))
    .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (v1,v2)->v1, LinkedHashMap::new));

Solution 3

Stream has as sorted method which accepts a comparator hence you can directly use the comparator as (x,y)->y.getKey().compareTo(x.getKey()) for descending sorting. To sort the map in ascending we can reverse the ordering as (x,y)->x.getKey().compareTo(y.getKey())

for consolidating result back into LinkedHashMap we can use Collectors toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier) which Returns a Collector that accumulates elements into a Map whose keys and values are the result of applying the provided mapping functions to the input elements.

Working code

import java.io.*;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.*;
public class HelloWorld{

     public static void main(String []args){
        LinkedHashMap<Integer,Integer> hashMap = new LinkedHashMap<Integer,Integer>(); 
            hashMap.put(1,5);
            hashMap.put(7,9);
            hashMap.put(3,8);
            hashMap.put(10,5);

            Function<Map.Entry<Integer,Integer>,Integer> keyMapper = x->x.getKey();
            Function<Map.Entry<Integer,Integer>,Integer> valueMapper = x->x.getValue();
            BinaryOperator< Integer> mergeFunction = (x,y)->x;// we do not want any merging here 
            Supplier<LinkedHashMap<Integer,Integer>> mapRequired =()-> {return new LinkedHashMap<Integer,Integer>();};// to maintain order we must use LinkedHashMap
            Comparator<Map.Entry<Integer,Integer>> descendingComparator = (x,y)->y.getKey().compareTo(x.getKey());
            // we can write it as  

        System.out.println(
                  hashMap.entrySet().stream()
                             .sorted (descendingComparator) 
                             .collect(Collectors.toMap(
                                                      keyMapper,
                                                       valueMapper,
                                                       mergeFunction,
                                                       mapRequired)
                                           )
                );           

// or even by writing below will also work

        System.out.println(
                  hashMap.entrySet().stream()
                             .sorted ((x,y)->y.getKey().compareTo(x.getKey())) 
                             .collect(Collectors.toMap(
                                                       x->x.getKey(),
                                                       x->x.getValue(),
                                                       (x,y)->x,
                                                       LinkedHashMap::new)
                                           )
                );           
     }


}

Solution 4

Since Java 1.8 java.util.Comparator.reversed()

myMap.entrySet().stream()
.sorted(Map.Entry.comparingByValue().reversed())
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
Share:
18,589

Related videos on Youtube

Oxydron
Author by

Oxydron

I am a professional with great analytical, logical and creative skills, who likes to learn new concepts, technologies and paradigms. I keep myself updated in the field of AI and data science through the best scientific articles available and I like to be at the forefront in the use of the best technologies and algorithms for the development of any solution. I believe I'm a good addition to any software development company and especially if there is a focus on research and development in the fields of machine learning and computer vision. I have a bachelor and master degree on computer science from Federal University of Lavras, Brazil.

Updated on June 16, 2022

Comments

  • Oxydron
    Oxydron almost 2 years

    To sort it int ascending order I can use:

    myMap.entrySet().stream()
        .sorted(Map.Entry.comparingByValue())
        .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
    

    How can I do it in decreasing order?

  • Grzegorz Piwowarek
    Grzegorz Piwowarek about 9 years
    This answer is the only valid one.
  • Alexis C.
    Alexis C. about 9 years
    As its name indicates (comparingByValue), the type parameters of the lambda expression is V not Entry<K,V> because you provide a comparator on the values, so you might change it to comparingByValue((v1, v2)->v2.compareTo(v1)) or better use comparingByValue(reverseOrder()) as @Misha suggested...
  • Alexis C.
    Alexis C. about 9 years
    .. and as you said a quick look at the source code of toMap you'll see that it returns toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);. So basically your implementation won't do something very useful (although you took it from the OP's question)...
  • Eran
    Eran about 9 years
    @AlexisC. Thanks for the comment. I totally missed that. That's what happens when I write code without trying to compile it.
  • Oxydron
    Oxydron about 9 years
    Thanks, I didn't know that I could use this Comparator.reverserOrder(). In fact I thought that would exist a direct way to do this, like .sorted(Map.Entry.comparingByValueReverse())
  • johnlemon
    johnlemon about 5 years
    this is not correct;should be Map.Entry.comparingByValue(Comparator.reverseOrder()))
  • b15
    b15 almost 5 years
    doesn't compile