Упорядоченная карта Java

322

Есть ли в Java объект, который действует как карта для хранения и доступа к парам ключ / значение, но может возвращать упорядоченный список ключей и упорядоченный список значений, чтобы списки ключей и значений были в одном и том же порядке?

В качестве объяснения по коду я ищу что-то похожее на мой вымышленный OrderedMap:

OrderedMap<Integer, String> om = new OrderedMap<>();
om.put(0, "Zero");
om.put(7, "Seven");

String o = om.get(7); // o is "Seven"
List<Integer> keys = om.getKeys();
List<String> values = om.getValues();

for(int i = 0; i < keys.size(); i++)
{
    Integer key = keys.get(i);
    String value = values.get(i);
    Assert(om.get(key) == value);
}
Что это
источник
4
Если все, что вы хотите сделать, это выполнять итерацию по обоим одновременно, то Map.entrySet () позволит вам сделать это на любой карте. LinkedHashMap имеет четко определенный порядок, но для любой карты набор записей отражает пары ключ / значение.
Пит Киркхэм
5
Этот код не является хорошим примером, так как любая реализация Map будет вести себя как пример кода. отсортировано, заказано или нет.
Питер Лори
2
В реализации JDK для Sun наборы, возвращаемые наборами getKeys и getValues ​​(), поддерживаются entrySet () на карте, поэтому они будут иметь тот же порядок итераций, что и в ваших тестовых примерах.
Пит Киркхэм
4
Ну, это интересно, я никогда этого не замечал. Тем не менее, называйте меня сумасшедшим, но я предпочитаю не делать предположений о реализации, которые явно не проверены интерфейсом. Меня слишком много раз сжигали, делая это в прошлом.
Whatsit
2
Это должно называться Java Sorted Map, так как Ordered Map - это нечто иное - смотрите LinkedHashMap.
Ондра Жижка

Ответы:

398

Интерфейс SortedMap (с реализацией TreeMap ) должен быть вашим другом.

Интерфейс имеет методы:

  • keySet() который возвращает набор ключей в порядке возрастания
  • values() который возвращает коллекцию всех значений в порядке возрастания соответствующих ключей

Так что этот интерфейс полностью соответствует вашим требованиям. Однако ключи должны иметь осмысленный порядок. В противном случае вы можете использовать LinkedHashMap, где порядок определяется порядком вставки.

dmeister
источник
2
пример: SortedMap <String, Object> map = new TreeMap <> ();
Бен
7
Чтобы использовать TreeMap, необходимо, чтобы в классе ключей был реализован интерфейс Comparable. Если нет, то будет выдано какое-то RuntimeException. TreeMap это также отсортированная карта, но я думаю, что автор хочет использовать только что упорядоченную (не отсортированную) карту. LinkedHashMap это хороший выбор, чтобы получить только упорядоченную карту (как вы сказали, «определяется порядком вставки»).
К. Гол
1
этот ответ может быть улучшен путем показа итерации по keySet ()
4
Из Ява 8 док . LinkedHashMapчей порядок итераций равен порядку, в котором к последним элементам обращались
TRiNE
1
@TRiNE Я не слежу за вашим комментарием, но, возможно, пропустил какой-то контекст. По умолчанию LinkedHashMapпорядок итераций - порядок вставки, но вы можете использовать другой конструктор, чтобы указать порядок доступа. docs.oracle.com/javase/8/docs/api/java/util/...
грабят
212

Существует ли объект, который действует как карта для хранения и доступа к парам ключ / значение, но может возвращать упорядоченный список ключей и упорядоченный список значений, чтобы списки ключей и значений были в одном и том же порядке?

Вы ищете java.util.LinkedHashMap . Вы получите список пар Map.Entry <K, V> , которые всегда повторяются в одном и том же порядке. Этот порядок совпадает с порядком, в котором вы помещаете элементы. В качестве альтернативы используйте java.util.SortedMap , где ключи должны либо иметь естественный порядок, либо указывать его с помощью Comparator.

Джон Феминелла
источник
14
И чтобы просто сохранить читателя, дважды проверяя это, потому что это трудно проверить путем тестирования, keySet()метод эффективно возвращает LinkedHashSet, который отражает порядок ваших put()вызовов. Обратите внимание, что повторные вызовы put()для одного и того же ключа не изменят порядок, если вы не remove()нажали ключ заранее.
Гленн Лоуренс
Из Ява 8 док . LinkedHashMapчей порядок итерации - это порядок, к которому в последний раз обращались к его записям
TRiNE
24

LinkedHashMap поддерживает порядок ключей.

java.util.LinkedHashMap, кажется, работает так же, как обычный HashMap в противном случае.

VoNWooDSoN
источник
1
Это не дает ответа на вопрос. Чтобы критиковать или запрашивать разъяснения у автора, оставьте комментарий под его постом - вы всегда можете комментировать свои собственные посты, и, когда у вас будет достаточно репутации, вы сможете комментировать любой пост .
ianaya89
2
@ ianaya89 Я думаю, что это реальный ответ, но он очень похож на ответ Джона Феминеллы !
T30
1
Если вы хотите получить упорядоченную карту, где записи хранятся в том порядке, в котором вы их поместили на карту, то LinkedHashMap является правильным ответом. Если вы хотите отсортировать записи в вашей карте независимо от того, в каком порядке вы их поместили, то SortedMap является правильным ответом.
Ральф
@TRiNE Связанный с java 8 документ говорит, что возможны два режима упорядочения: порядок вставки и порядок доступа. Вы думаете о последнем, который вызывается с помощью специального конструктора public LinkedHashMap (int initialCapacity, float loadFactor, логический accessOrder). Конструктор по умолчанию создает упорядоченный при вставке экземпляр LinkedHashMap.
Артур Жисик
1
@ ArturŁysik да, это так. Это была ошибка, сделанная мной несколько дней назад. Я исправлю это. Я удаляю комментарий. как я больше не могу его редактировать
TRINE
7

Я думаю, что ближайшая коллекция, которую вы получите из фреймворка, это SortedMap

Бруно Конде
источник
3
Я бы проголосовал против этого ответа, если бы подумал, что стоит потерять за него очки. Как указывает приведенный выше ответ, в вашем ответе отсутствует правильная информация о LinkedHashMap, и небольшое объяснение SortedMap также было бы неплохо.
CorayThan
@CorayThan, в этом случае вы одобряете лучшие ответы, а не понижаете голосом других, которые могут быть правильными, но не лучшими ...
bruno conde
1
Это то, что я сделал. Просто скажу, что я могу понять, почему кто-то голосовал против.
CorayThan
5

Вы можете использовать интерфейс NavigableMap, к которому можно получить доступ и пройти в порядке возрастания или убывания ключа. Этот интерфейс предназначен для замены интерфейса SortedMap. Навигационная карта обычно сортируется в соответствии с естественным порядком ее ключей или с помощью компаратора, предоставляемого во время создания карты.

Существует три наиболее полезных реализации этого: TreeMap , ImmutableSortedMap и ConcurrentSkipListMap .

Пример TreeMap:

TreeMap<String, Integer> users = new TreeMap<String, Integer>();
users.put("Bob", 1);
users.put("Alice", 2);
users.put("John", 3);

for (String key: users.keySet()) {
  System.out.println(key + " (ID = "+ users.get(key) + ")");
}

Вывод:

Alice (ID = 2)
Bob (ID = 1)
John (ID = 3)
Виталий Федоренко
источник
4

Начиная с Java 6 существует неблокирующая поточно-ориентированная альтернатива TreeMap . Смотрите ConcurrentSkipListMap .

Vadzim
источник
2

ТЛ; др

Чтобы сохранить Map< Integer , String >порядок, отсортированный по ключу, используйте любой из двух классов, реализующих интерфейсы SortedMap/ NavigableMap:

  • TreeMap
  • ConcurrentSkipListMap

Если вы манипулируете картой внутри одного потока, используйте первый TreeMap,. При манипулировании потоками используйте второе ConcurrentSkipListMap,.

Для получения дополнительной информации см. Таблицу ниже и последующее обсуждение.

подробности

Вот графическая таблица, которую я сделал, показывающий возможности десяти Mapреализаций, связанных с Java 11.

NavigableMapИнтерфейс , что SortedMapдолжно быть в первую очередь. SortedMapЛогически должны быть удалены , но не может быть , как некоторые карты реализации третьей стороны может использовать интерфейс.

Как видно из этой таблицы, только два класса реализуют интерфейсы SortedMap/ NavigableMap:

Оба из них хранят ключи в отсортированном порядке, либо в их естественном порядке (используя compareToметод Comparable( https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ Comparable.html ) или через Comparatorреализацию, которую вы передаете. Разница между этими двумя классами заключается в том, что второй класс ConcurrentSkipListMapявляется потокобезопасным и очень параллельным .

См. Также столбец « Порядок итераций» в таблице ниже.

  • LinkedHashMapКласс возвращает свои записи по порядку , в котором они были первоначально вставлены .
  • EnumMapвозвращает записи в порядке, в котором определяется класс перечисления ключа . Например, карта того, какой сотрудник покрывает, какой день недели ( Map< DayOfWeek , Person >) использует DayOfWeekкласс enum, встроенный в Java. Это перечисление определяется первым понедельником и последним воскресеньем. Таким образом, записи в итераторе будут появляться в таком порядке.

Другие шесть реализаций не дают никаких обещаний относительно порядка, в котором они сообщают свои записи.

Таблица реализаций карт в Java 11, сравнение их возможностей

Базилик Бурк
источник
0

Я использовал карту Simple Hash, связанный список и коллекции для сортировки карты по значениям.

import java.util.*;
import java.util.Map.*;
public class Solution {

    public static void main(String[] args) {
        // create a simple hash map and insert some key-value pairs into it
        Map<String, Integer> map = new HashMap<String, Integer>();
        map.put("Python", 3);
        map.put("C", 0);
        map.put("JavaScript", 4);
        map.put("C++", 1);
        map.put("Golang", 5);
        map.put("Java", 2);
        // Create a linked list from the above map entries
        List<Entry<String, Integer>> list = new LinkedList<Entry<String, Integer>>(map.entrySet());
        // sort the linked list using Collections.sort()
        Collections.sort(list, new Comparator<Entry<String, Integer>>(){
        @Override
         public int compare(Entry<String, Integer> m1, Entry<String, Integer> m2) {
        return m1.getValue().compareTo(m2.getValue());
        }
      });
      for(Entry<String, Integer> value: list) {
         System.out.println(value);
     }
   }
}

Выход:

C=0
C++=1
Java=2
Python=3
JavaScript=4
Golang=5
Штеффи Керан Рани Дж
источник