Получить минимальное значение карты (ключ, двойной)

Есть ли способ (возможно, с помощью Google Collections) получить минимальное значение Map(Key, Double)?

Традиционным способом мне пришлось бы сортировать карту по значениям и брать первое/последнее.


person user326667    schedule 05.05.2010    source источник


Ответы (9)


Вы можете использовать стандартный Collections#min() для этого.

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

Обновление: поскольку вам также нужен ключ, я не вижу способов в Collections или Google Collections2 API, поскольку Map не является Collection. Maps#filterEntries() также не очень полезен, так как вы знаете фактический результат только в конце итерации.

Тогда самым простым решением будет следующее:

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

(нулевая проверка на min оставлена ​​в стороне)

person BalusC    schedule 05.05.2010
comment
Спасибо за ответ - я забыл упомянуть, что мне нужен ключ минимального значения - person user326667; 05.05.2010
comment
Большое спасибо, кстати, мне тоже нужно было бы получить ранги отсортированной карты. Я хотел бы использовать индекс как ранг (1,2,3...). Что может быть лучшим способом сделать это с картами, поскольку здесь невозможно получить индекс. Довольно неудобно каждый раз создавать список из карты - person user326667; 06.05.2010
comment
Если вам нужен индекс, вам действительно нужно использовать List<SomeObject> вместо него в сочетании с подходящим Comparator<SomeObject> или, может быть, Comparable<SomeObject>. SomeObject, в свою очередь, может содержать исходный ключ карты и значение. Set<SomeObject> тоже может подойти, только индекс посчитаете сами. - person BalusC; 06.05.2010

Вы по-прежнему можете использовать Collections.min с пользовательским Comparator, чтобы получить Map.Entry с меньшим значением:

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);
Entry<String, Double> min = Collections.min(map.entrySet(), new Comparator<Entry<String, Double>>() {
    public int compare(Entry<String, Double> entry1, Entry<String, Double> entry2) {
        return entry1.getValue().compareTo(entry2.getValue());
    }
});
System.out.printf("%s: %f", min.getKey(), min.getValue()); // 0.1: 0.100000

С Java 8:

Entry<String, Double> min = Collections.min(map.entrySet(),
                                       Comparator.comparing(Entry::getValue));
person superfav    schedule 06.05.2010

Однострочный Java8

Key key = Collections.min(map.entrySet(), Map.Entry.comparingByValue()).getKey()
person Ankit Sharma    schedule 03.05.2019

Традиционным способом мне пришлось бы сортировать карту по значениям и брать первое/последнее. спасибо

Нет, ты бы не стал. Вам придется перебирать все значения и на каждом этапе сравнивать текущий элемент с наименьшим из виденных до сих пор. Это O(n) по сравнению с O(n*log(n)) для сортировки — потенциально огромная разница.

Кстати, именно так работает Collections.min().

person Michael Borgwardt    schedule 05.05.2010
comment
Если я сортирую карту по значениям, я получаю новую отсортированную карту, например: (A,1),(B,2),(C,3) Здесь я могу взять только ключ первого элемента. - person user326667; 06.05.2010
comment
@chris-gr: это занимает гораздо больше времени и использует больше памяти, чем необходимо. - person Michael Borgwardt; 06.05.2010

Использование потоков Java 8:

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);

Но если вы хотите сделать это несколько раз, обязательно посмотрите heap.

person voho    schedule 18.07.2016

Я был бы склонен использовать Google Collections BiMap:

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

Или что-то в этом роде (не проверено).

person Yishai    schedule 05.05.2010
comment
Хороший oneliner, но не очень эффективный. - person BalusC; 06.05.2010
comment
Но может быть и так, что значения одинаковые (двойные) Какой ключ я получу в этом случае? - person user326667; 06.05.2010
comment
@chris, это связано с тем, что является ключом и каково значение на карте, а не с их типами. В конце концов, во время выполнения они все равно являются объектами. - person Yishai; 06.05.2010
comment
@BalusC, конечно нет, но это может и не иметь значения. Если карта большая и часто используется в двух направлениях, она может быть сохранена в первую очередь в BiMap. inverse() в этом случае является дешевой операцией. - person Yishai; 06.05.2010

В Java 8 мы можем легко получить:

Double minValue = map.entrySet().stream().min(Map.Entry.comparingByValue()).get().getValue();
Double maxValue = map.entrySet().stream().max(Map.Entry.comparingByValue()).get().getValue();
person Az.MaYo    schedule 20.01.2019
comment
хорошо, но может привести к ошибкам при использовании .get(). Они создают 'Optional.get()' без предупреждения 'isPresent()'. Посетите эту страницу, чтобы найти более безопасные альтернативы. - person spectrum; 02.02.2019

Чтобы сделать это эффективно, вы можете определить свою собственную структуру данных, чтобы она реализовывала интерфейс карты, но также позволяла эффективную операцию getMin().

Это можно сделать с помощью двух внутренних структур данных: карты и дерева (или структуры данных кучи). Каждый раз, когда добавляется новая пара (K,V), добавляйте их на карту, а также в дерево (как одну запись). Это дает время O(1) для операций get(Key) и время O(log n) для операций добавления, удаления и getMin.

person Eyal Schneider    schedule 05.05.2010

Использование java 8 (и статический импорт). Мы можем сделать решение @superfav более аккуратным:

Map<String, Double> myMap;
String theKeyWithHighestValue = Collections.min(myMap.entrySet(), comparingDouble(Entry::getValue)).getKey()
person Tarrasch    schedule 12.04.2016