3

In my code, I've got a few maps. Then I have a method like the one below, which takes one of the maps, sorts the entries by their value and returns a list of the top ones, with the amount given by the parameter.

Example:

if the input map is like

  • "a" = 5,
  • "b" = 4,
  • "c" = 8,
  • "d" = 0,

and I call the method with quantity = 2 in the parameter, I'm given a list of the 2 highest map entries, sorted decreasingly

  • "c" = 8,
  • "a" = 5.

Right now, I have one such method for each of the maps and they only differ in:

  • the <Type1, Type2> declarations all over the method, and
  • the (distances.entrySet()); population of the all list.

Can I generalize this somehow to have just one alike method, being able to receive any of the types?

private static Map<String, Double>      distances = new TreeMap<>();
private static Map<String, Integer>     titles    = new TreeMap<>();
private static Map<Integer, Integer>    hours     = new TreeMap<>();
private static Map<Date, Integer>       days      = new TreeMap<>();

public static List<Entry<String, Double>> getTopDistances(int quantity) {
    List<Map.Entry<String, Double>> all       = new ArrayList<>(distances.entrySet());
    List<Map.Entry<String, Double>> requested = new ArrayList<>();

    Collections.sort(all, new Comparator<Map.Entry<String, Double>>() {
        @Override
        public int compare(Entry<String, Double> e1, Entry<String, Double> e2) {
            return (e2.getValue().compareTo(e1.getValue()));
        }

    });

    int i = 0;
    while (all.iterator().hasNext() && ++i <= quantity) {
        requested.add(all.get(i - 1));
    }

    return requested;
}

I can surely continue with all the methods separated, but I sense a better way of doing it. Have researched generics, wildcards, collections and interfaces, which I think is the way to go, yet I still need a push.

David Hudec
  • 440
  • 4
  • 8
  • why are you using override ? just send the comparator with the treemap when u intantiate it ? see [TreeMap Constructor](http://docs.oracle.com/javase/7/docs/api/java/util/TreeMap.html#TreeMap(java.util.Comparator)) – Mikenno Mar 25 '17 at 13:52
  • You said "sorts the entries by their key and returns a list of the top ones" but in your example you sort by the value. – Alon Segal Mar 25 '17 at 13:55
  • You're right, I described it wrong. I'm sorting by values, like the code says. – David Hudec Mar 25 '17 at 14:02

2 Answers2

2

With Java Streams one single line can produce what you want from the map to the final list. Bellow I package it in a private method which is a bit more neat but you could inline it if you want.

Since all your values are Comparable:

private <K, V extends Comparable<V>> List<Map.Entry<K,V>> getTop(Map<K,V> map, int quantity) {
    return map.entrySet().stream()
           .sorted((a,b) -> b.getValue().compareTo(a.getValue()))
           .limit(quantity)
           .collect(Collectors.toList());
} 

If the value type was not comparable you then would need to pass the comparator as an additional parameter:

private <K, V> List<Map.Entry<K,V>> getTop(Map<K,V> map, Comparator<V> cmp, int quantity) {
    return map.entrySet().stream()
           .sorted((a,b) -> cmp.compare(b,a))
           .limit(quantity)
           .collect(Collectors.toList());
} 
Valentin Ruano
  • 2,726
  • 19
  • 29
1

You cannot create comparator who will compare two comparables with different types, so you have to do this like this:

private static Map<String, Double> distances = new TreeMap<>();
private static Map<String, Integer>     titles    = new TreeMap<>();
private static Map<Integer, Integer>    hours     = new TreeMap<>();
private static Map<Date, Integer>       days      = new TreeMap<>();

public static List<Map.Entry<String, Double>> getTopDistances(int quantity) {
    List<Map.Entry<String, Double>> all       = new ArrayList<>(distances.entrySet());
    List<Map.Entry<String, Double>> requested = new ArrayList<>();

    all.sort(naturalOrder());

    int i = 0;
    while (all.iterator().hasNext() && ++i <= quantity) {
        requested.add(all.get(i - 1));
    }

    return requested;
}

public static <T extends Comparable<? super T>> Comparator<Map.Entry<?,T> naturalOrder() {
    return (e1, e2) -> e2.getValue().compareTo(e2.getValue());
}
carbolymer
  • 1,439
  • 1
  • 15
  • 30
  • So far I like Valentin's suggestion the most, as it is just what I looked for. But I'll read through yours as well, of course! Thanks. – David Hudec Mar 25 '17 at 14:38