4

I am actually able to sort my Map on the basis of both Key and value alone, I even tried to sort them like below:

I sorted the students on the basis of country , and if two students happen to have same states then sort by StudentID only among the matched country.

What i have tried so far:

final Map<Integer, String> studentMaster = new HashMap<>() {{
        put(146, "Sweden");
        put(148, "Sweden");
        put(110, "Orebro");
        put(6, "Malmo");
        put(14, "Orebro");
        put(26, "Malmo");
    }   
    };
    studentMaster.entrySet().stream()
    .sorted((i1,i2)->i1.getValue().compareTo(i2.getValue()))
    .sorted((j1,j2)->j1.getKey().compareTo(j2.getKey()))
    .forEach(System.out::println);

The result that I am getting**( actual output )**

14=Orebro
26=Malmo
110=Orebro
146=Sweden
148=Sweden

Expected Output:

  26=Malmo
  14=Orebro
  110=Orebro
  146=Sweden
  148=Sweden
Naman
  • 27,789
  • 26
  • 218
  • 353
  • If not needed to store the entries sorted, then custom comparator should be just enough – HamoriZ Feb 01 '19 at 16:15
  • @CaseyRule I don't believe this is a duplicate, as the OP wants to sort by value **and then** by key. – Jacob G. Feb 01 '19 at 16:43
  • @JacobG. I had actually identified https://stackoverflow.com/questions/3074154/sorting-a-hashmap-based-on-value-then-key as duplicating this question, but that question was flagged as a duplicate of https://stackoverflow.com/questions/109383/sort-a-mapkey-value-by-values. It looks like the transitive property of duplication has not held in this case :) – Casey Rule Feb 01 '19 at 16:47
  • @CaseyRule Ah, that's funny; good catch! – Jacob G. Feb 01 '19 at 16:47
  • your problem solved or not?? – Vishwa Ratna Feb 04 '19 at 05:28
  • You first need to sort the result on the basis of value and then if the values are same then for them on the basis of Key. – Arpit Aug 23 '23 at 06:49

4 Answers4

3

Note: Your expected and actual outputs don't match up with the keys that you added to your Map.


The reason that your code doesn't work is because you're calling Stream#sorted twice with two separate Comparators, so the first call to Stream#sorted is useless in your case (as it's overridden by the second call).


I was able to achieve your expected output by passing a custom Comparator to Stream#sorted:

Map.Entry.<Integer, String>comparingByValue()
    .thenComparing(Map.Entry.comparingByKey())

Output:

6=Malmo
26=Malmo
14=Orebro
110=Orebro
146=Sweden
148=Sweden
Jacob G.
  • 28,856
  • 5
  • 62
  • 116
2

Sometime back i gave answer to How to sort the name along with age in java , Many similarities to your question apart from data-structure used for storage. To traverse in each key and sort it and then again in value and then sort it is quite tedious and can get you hell a lot confused. just remember how you used to traverse in Map when you did not used to use Stream :

for (Map.Entry<String,String> entry : somemap.entrySet()){..Some Statements..};

studentMaster.entrySet().stream()
    .sorted(Comparator.comparing((Map.Entry<Integer, String> m) -> m.getValue())
              .thenComparing(Map.Entry::getKey)).forEach(System.out::println);

Output

6=Malmo
26=Malmo
14=Orebro
110=Orebro
146=Sweden
148=Sweden
Vishwa Ratna
  • 5,567
  • 5
  • 33
  • 55
1

The Comparator should look like this:

Comparator<Entry<Integer, String>> comparator = (o1, o2) -> {
    int i = o1.getValue().compareTo(o2.getValue());
    if (i == 0) {
        return o1.getKey().compareTo(o2.getKey());
    } else {
        return i;
    }
};

And then pass it to the Stream#sorted method: studentMaster.entrySet().stream().sorted(comparator).forEach(System.out::println);

Output:

6=Malmo
26=Malmo
14=Orebro
110=Orebro
146=Sweden
148=Sweden
Jorj
  • 1,291
  • 1
  • 11
  • 32
0

2 ways:

  • Use TreeSet with Comparable pojo.
  • Use TreeSet with customized Comparator.

Code

Tmp.java

(Use TreeSet with Comparable pojo.)

import java.util.*;

public class Tmp {
    static class StudentMaster implements Comparable<StudentMaster> {
        private Integer id;
        private String master;

        public StudentMaster(Integer id, String master) {
            this.id = id;
            this.master = master;
        }

        @Override
        public int compareTo(StudentMaster other) {
            int masterFlag = master.compareTo(other.master);
            return (masterFlag == 0) ? id.compareTo(other.id) : masterFlag;
        }

        @Override
        public boolean equals(Object o) {
            StudentMaster osm = (StudentMaster) o;
            return id == osm.id && master.equals(osm.master);
        }

        @Override
        public int hashCode() {
            return Objects.hash(id, master);
        }

        public String toString() {
            StringBuilder sb = new StringBuilder();
            Formatter fm = new Formatter(sb);

            fm.format("id = %d, master = %s\n", id, master);

            fm.close();
            return sb.toString();
        }
    }

    public static void test() {
        final Set<StudentMaster> smSet = new TreeSet<>();

        smSet.add(new StudentMaster(146, "Sweden"));
        smSet.add(new StudentMaster(148, "Sweden"));
        smSet.add(new StudentMaster(110, "Orebro"));
        smSet.add(new StudentMaster(6, "Malmo"));
        smSet.add(new StudentMaster(14, "Orebro"));
        smSet.add(new StudentMaster(26, "Malmo"));

        for (StudentMaster sm : smSet) {
            System.out.print(sm);
        }
    }

    public static void main(String[] args) {
        test();
    }
}

TmpComparator.java

(Use TreeSet with customized Comparator.)

import java.util.*;

public class TmpComparator {
    static Comparator<StudentMaster> smc = new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            StudentMaster sm1 = (StudentMaster) o1, sm2 = (StudentMaster) o2;

            int masterFlag = sm1.master.compareTo(sm2.master);
            return (masterFlag == 0) ? sm1.id.compareTo(sm2.id) : masterFlag;
        }
    };

    static class StudentMaster {
        private Integer id;
        private String master;

        public StudentMaster(Integer id, String master) {
            this.id = id;
            this.master = master;
        }

        @Override
        public boolean equals(Object o) {
            StudentMaster osm = (StudentMaster) o;
            return id == osm.id && master.equals(osm.master);
        }

        @Override
        public int hashCode() {
            return Objects.hash(id, master);
        }

        public String toString() {
            StringBuilder sb = new StringBuilder();
            Formatter fm = new Formatter(sb);

            fm.format("id = %d, master = %s\n", id, master);

            fm.close();
            return sb.toString();
        }
    }

    public static void test() {
        final Set<StudentMaster> smSet = new TreeSet<>(smc);

        smSet.add(new StudentMaster(146, "Sweden"));
        smSet.add(new StudentMaster(148, "Sweden"));
        smSet.add(new StudentMaster(110, "Orebro"));
        smSet.add(new StudentMaster(6, "Malmo"));
        smSet.add(new StudentMaster(14, "Orebro"));
        smSet.add(new StudentMaster(26, "Malmo"));

        for (StudentMaster sm : smSet) {
            System.out.print(sm);
        }
    }

    public static void main(String[] args) {
        test();
    }
}

Just run the main() method.

Output of both are the same:

id = 6, master = Malmo
id = 26, master = Malmo
id = 14, master = Orebro
id = 110, master = Orebro
id = 146, master = Sweden
id = 148, master = Sweden

Tips

  • In production code, the equals() need to be improved, this is a simplified version for test only.
Eric
  • 22,183
  • 20
  • 145
  • 196