36

Is there a method (maybe with Google Collections) to obtain the min value of a Map(Key, Double)?

In the traditional way, I would have to sort the map according to the values, and take the first/last one.

peterh
  • 11,875
  • 18
  • 85
  • 108
user326667
  • 509
  • 1
  • 7
  • 11

9 Answers9

64

You can use the standard Collections#min() for this.

Map<String, Double> map = new HashMap<String, Double>();
map.put("1.1", 1.1);
map.put("0.1", 0.1);
map.put("2.1", 2.1);

Double min = Collections.min(map.values());
System.out.println(min); // 0.1

Update: since you need the key as well, well, I don't see ways in Collections or Google Collections2 API since a Map is not a Collection. The Maps#filterEntries() is also not really useful, since you only know the actual result at end of iteration.

Most straightforward solution would then be this:

Entry<String, Double> min = null;
for (Entry<String, Double> entry : map.entrySet()) {
    if (min == null || min.getValue() > entry.getValue()) {
        min = entry;
    }
}

System.out.println(min.getKey()); // 0.1

(nullcheck on min left aside)

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thanks for the answer - I forgot to mention that I need the key of the min value – user326667 May 05 '10 at 19:52
  • Thanks a lot, by the way, I would also need to get ranks of a sorted map. I would like to use the index as rank (1,2,3...) What might be the best way to do this with maps as here its not possible to obtain the index. It is rather inconvinient to create a list from the map each time – user326667 May 05 '10 at 20:13
  • 1
    If you want an index, you'll really need to use a `List` instead in combination with a suitable `Comparator` or maybe `Comparable`. The `SomeObject` in turn can then hold the original map key and value. A `Set` may also be suitable, you'll only count the index yourself. – BalusC May 05 '10 at 20:18
27

You still can use Collections.min with a custom Comparator to get the Map.Entry with the lower value:

Map<String, Double> map = new HashMap<String, Double>();
map.put("1.1", 1.1);
map.put("0.1", 0.1);
map.put("2.1", 2.1);
Map.Entry<String, Double> min = Collections.min(map.entrySet(), new Comparator<Map.Entry<String, Double>>() {
    public int compare(Map.Entry<String, Double> entry1, Map.Entry<String, Double> entry2) {
        return entry1.getValue().compareTo(entry2.getValue());
    }
});
System.out.printf("%s: %f", min.getKey(), min.getValue()); // 0.1: 0.100000

With Java 8+:

Map.Entry<String, Double> min = Collections.min(map.entrySet(),
                                                Map.Entry.comparingByValue());
superfav
  • 1,041
  • 10
  • 11
15

Java8 One-Liner

Key key = Collections.min(map.entrySet(), Map.Entry.comparingByValue()).getKey()
Ankit Sharma
  • 1,626
  • 1
  • 14
  • 21
5

In traditional way, I would have to sort the map according to the values, and take the first/last one. thanks

No, you wouldn't. You would have to iterate through all values and at each step compare the current element with the smallest one seen so far. That's O(n), compared with O(n*log(n)) for sorting - a potentially huge difference.

BTW, this is exactly how Collections.min() works.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
4

Using Java 8 streams:

return map
            .entrySet()
            .stream()
            .sorted(Comparator.comparingDouble(Map.Entry::getValue))
            .findFirst()
            .map(Map.Entry::getValue);

Or

return map
            .entrySet()
            .stream()
            .min(Comparator.comparingDouble(Map.Entry::getValue))
            .map(Map.Entry::getValue);

But if you want to do it multiple times, then definitely give heap a look.

voho
  • 2,805
  • 1
  • 21
  • 26
2

I'd be inclined to use a Google Collections BiMap:

     String minKey = HashBiMap.create(map).inverse().get(Collections.min(map.values()));

Or something like that (not tested).

Yishai
  • 90,445
  • 31
  • 189
  • 263
  • Nice oneliner, but not really efficient. – BalusC May 05 '10 at 20:20
  • But it can be that the values are of the same value (double) Which key do I get in this case? – user326667 May 05 '10 at 20:23
  • @chris, it has to do with what is the key and what is the value in the map, not their types. In the end, at runtime, they are all Object anyway. – Yishai May 05 '10 at 20:32
  • @BalusC, Certainly not, but that may not matter. If the map is large and often used bidirectionally, it could be stored in a BiMap in the first place. inverse() is a cheap operation in that case. – Yishai May 05 '10 at 20:33
2

Using java 8 (and static imports). We can make @superfav's solution much tidier:

Map<String, Double> myMap;
String theKeyWithHighestValue = Collections.min(myMap.entrySet(), comparingDouble(Entry::getValue)).getKey()
Tarrasch
  • 10,199
  • 6
  • 41
  • 57
2

In Java 8 we can get easily:

Double minValue = map.entrySet().stream().min(Map.Entry.comparingByValue()).get().getValue();
Double maxValue = map.entrySet().stream().max(Map.Entry.comparingByValue()).get().getValue();
Az.MaYo
  • 1,044
  • 10
  • 23
  • 1
    nice, but can lead to errors using `.get()`. These create an 'Optional.get()' without 'isPresent()' warning. Check out [this page](https://stackoverflow.com/questions/30686215/avoid-nosuchelementexception-with-stream) for some safer alternatives – spectrum Feb 02 '19 at 17:49
1

In order to do it efficiently, you may want to define your own data structure, such that it implements the Map interface,but also allows efficient getMin() operation.

This can be done using two internal data structures: a map and a tree (or heap data structure). Each time a new pair (K,V) is added, add them to the map, and also to the tree (as a single entry). This allows O(1) time for get(Key) operations, and O(log n) time for addition, removal, and getMin operations.

Eyal Schneider
  • 22,166
  • 5
  • 47
  • 78