7

How can you sort a LinkedHashMap using the value ?

Is there a way to insert entries into a LinkedHashMap so that they are inserted in order based on their value ?

2
  • 2
    What is the purpose of using a LinkedHashMap when you sort it? Maybe use a TreeMap instead and provide an own Comparator to sort the value instead of the key. Commented Nov 24, 2014 at 21:48
  • The whole point of it being "Linked" is to derive the benefits of a List, i.e. indexed access. If you want default sorting, you'll need a Tree-based data structure Commented Nov 24, 2014 at 21:48

4 Answers 4

17

How can you sort a LinkedHashMap using the value?

LinkedHashMap is not sorted, it is ordered by order of insertion.

If your goal is to reorder the Map, you might do something like

static <K, V> void orderByValue(
        LinkedHashMap<K, V> m, final Comparator<? super V> c) {
    List<Map.Entry<K, V>> entries = new ArrayList<>(m.entrySet());

    Collections.sort(entries, new Comparator<Map.Entry<K, V>>() {
        @Override
        public int compare(Map.Entry<K, V> lhs, Map.Entry<K, V> rhs) {
            return c.compare(lhs.getValue(), rhs.getValue());
        }
    });

    m.clear();
    for(Map.Entry<K, V> e : entries) {
        m.put(e.getKey(), e.getValue());
    }
}

We put all the entries in a List, sort the List, then put the entries back in the Map in the new order.

Here's a Java 8 translation for those inclined:

static <K, V> void orderByValue(
        LinkedHashMap<K, V> m, Comparator<? super V> c) {
    List<Map.Entry<K, V>> entries = new ArrayList<>(m.entrySet());
    m.clear();
    entries.stream()
        .sorted(Comparator.comparing(Map.Entry::getValue, c))
        .forEachOrdered(e -> m.put(e.getKey(), e.getValue()));
}

(Which, out of curiosity, can be condensed to, although it is less efficient):

static <K, V> void orderByValue(
        LinkedHashMap<K, V> m, Comparator<? super V> c) {
    new ArrayList<>(m.keySet()).stream()
        .sorted(Comparator.comparing(m::get, c))
        .forEachOrdered(k -> m.put(k, m.remove(k)));
}

Is there a way to insert entries into a LinkedHashMap so that they are inserted in order based on their value?

No. See above. LinkedHashMap is not sorted.

If your goal is to keep the Map sorted, you need to use a TreeMap; however there are problems with doing so. Entries in the Map need to have unique values. See here and here.

Sign up to request clarification or add additional context in comments.

6 Comments

Elegant and minimal. How about a Java 8 version too - as a challenge. :D
@LuiggiMendoza If you mean using a temp TreeMap to sort the LinkedHashMap (e.g. TreeMap<...> tm = new TreeMap<>(lhm, valueComparator); lhm.clear(); lhm.putAll(tm);), I don't really think it is a good idea. Either values then need to be unique or you need a hack Comparator.
Even eleganter -<grin>
@OldCurmudgeon Sure, the method could accept Map, but it wouldn't have an effect. I suppose it would be nice if Java had an interface like OrderedMap<K, V> which LinkedHashMap could implement, even if it was just a marker.
What's the point in declaring parameters in the function header like this -> static <K, V> void orderByValue? I just don't get it... isn't this syntax normally used for parametrized types? This chunk doesn't seem to have anything to parametrize, as far as I know... can somebody explain it, please?
|
2

I think the best way to sort a LinkedHashMap by value is to write a comparator that compares two Map.Entry<K,V> objects by value, then

Map.Entry<K,V>[] entries = (Map.Entry<K,V>[])map.entrySet().toArray();
Arrays.sort(entries, comparator);

The comparator would look something like

Comparator<Map.Entry<K,V>> comparator = new Comparator<Map.Entry<K,V>>() {
    @Override
    public int compare(Map.Entry<K,V> o1, Map.Entry<K,V> o2) {
        return o1.getValue().compareTo(o2.getValue());
    }
};

Basically, it's the obvious thing: create an array of all the key/value pairs in the map, and then sort it. NOTE: I haven't tested this.

As for the second question: this would require a special data structure that maintains the values in order. When you insert an element, it would set up the hash table and maintain a doubly-linked list of the elements in insertion order and set up some sort of AVL tree to keep the values in order like TreeSet. I don't think Java defines a class like this, but maybe there's one in a third-party library. It might be easiest to maintain two separate structures, a LinkedHashMap<K,V> and a TreeSet<Map.Entry<K,V>>.

Comments

2

Like the answer before me suggested a LinkedHashMap is not sorted it only holds the insertion order. You have to manually create your comparator.

now the question is what do you what to sort the Map by ? By an integer key...

Here is a example: (Tree Map)

Default Compare

           // Create a hash map
          TreeMap tm = new TreeMap();
          // Put elements to the map
          tm.put("person1", new Double(1));
          tm.put("person2", new Double(2));
          tm.put("person3", new Double(3));
          tm.put("person4", new Double(4));
          tm.put("person5", new Double(5));
          
          // Get a set of the entries
          Set set = tm.entrySet();
          // Get an iterator
          Iterator i = set.iterator();
          // Display elements
          while(i.hasNext()) {
             Map.Entry me = (Map.Entry)i.next();
             System.out.print(me.getKey() + ": ");
             System.out.println(me.getValue());
          }
          System.out.println();
          
          // Deposit 1000 into person5's account
          double balance = ((Double)tm.get("person5")).doubleValue();
          tm.put("person5", new Double(balance + 1000));
          System.out.println("person5's new balance: " +
          tm.get("person5"));

This tree will sort in the natural order of the keys. i.e. person1 threw person5

    person1: 1.00
    person2: 2.00
    person3: 3.00
    person4: 4.00
    person5: 5.00
    person5's new balance: 1005.00

Use a custom comparator:

public class MyTMCompUserDefine {
 
    public static void main(String a[]){
        //By using name comparator (String comparison)
        TreeMap<Empl,String> tm = new TreeMap<Empl, String>(new MyNameComp());
        tm.put(new Empl("Ram",3000), "RAM");
        tm.put(new Empl("John",6000), "JOHN");
        tm.put(new Empl("Crish",2000), "CRISH");
        tm.put(new Empl("Tom",2400), "TOM");
        Set<Empl> keys = tm.keySet();
        for(Empl key:keys){
            System.out.println(key+" ==> "+tm.get(key));
        }
        System.out.println("===================================");
        //By using salary comparator (int comparison)
        TreeMap<Empl,String> trmap = new TreeMap<Empl, String>(new MySalaryComp());
        trmap.put(new Empl("Ram",3000), "RAM");
        trmap.put(new Empl("John",6000), "JOHN");
        trmap.put(new Empl("Crish",2000), "CRISH");
        trmap.put(new Empl("Tom",2400), "TOM");
        Set<Empl> ks = trmap.keySet();
        for(Empl key:ks){
            System.out.println(key+" ==> "+trmap.get(key));
        }
    }
}
 
class MyNameComp implements Comparator<Empl>{
 
    @Override
    public int compare(Empl e1, Empl e2) {
        return e1.getName().compareTo(e2.getName());
    }
}
 
class MySalaryComp implements Comparator<Empl>{
 
    @Override
    public int compare(Empl e1, Empl e2) {
        if(e1.getSalary() > e2.getSalary()){
            return 1;
        } else {
            return -1;
        }
    }
}
 
class Empl{
     
    private String name;
    private int salary;
     
    public Empl(String n, int s){
        this.name = n;
        this.salary = s;
    }
     
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getSalary() {
        return salary;
    }
    public void setSalary(int salary) {
        this.salary = salary;
    }
    public String toString(){
        return "Name: "+this.name+"-- Salary: "+this.salary;
    }
}

Output:
Name: Crish-- Salary: 2000 ==> CRISH
Name: John-- Salary: 6000 ==> JOHN
Name: Ram-- Salary: 3000 ==> RAM
Name: Tom-- Salary: 2400 ==> TOM
===================================
Name: Crish-- Salary: 2000 ==> CRISH
Name: Tom-- Salary: 2400 ==> TOM
Name: Ram-- Salary: 3000 ==> RAM
Name: John-- Salary: 6000 ==> JOHN

1 Comment

I want to sort by the value so I though there was no point creating a tree map if I was going to override the default sort.
0

Convert LinkedHashMap to sorted List and then do what you want:

        LinkedHashMap<String, BigDecimal> topCosts = data.getTopCosts();
        List<Map.Entry<String, BigDecimal>> entries = topCosts.entrySet()
            .stream()
            .sorted((o1, o2) -> o2.getValue().compareTo(o1.getValue())) // desc
            .collect(Collectors.toList());
        for (Map.Entry<String, BigDecimal> entry : entries) {
            // do somting..
        }
    

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.