Как отсортировать значения карты по ключу в Java?

362

У меня есть карта, которая имеет строки для ключей и значений.

Данные похожи на следующее:

«question1», «1»,
«question9», «1»,
«question2», «4»,
«question5», «2»

Я хочу отсортировать карту по ключам. Итак, в конце концов, у меня будет question1, question2, question3.... и так далее.


В конце концов, я пытаюсь получить две строки из этой карты.

  • Первая строка: вопросы (в порядке 1 .. 10)
  • Вторая строка: ответы (в том же порядке, что и вопрос)

Прямо сейчас у меня есть следующее:

Iterator it = paramMap.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry pairs = (Map.Entry) it.next();
    questionAnswers += pairs.getKey() + ",";
}

Это дает мне вопросы в строке, но они не в порядке.

n00bstackie
источник

Ответы:

613

Короткий ответ

Используйте TreeMap. Это именно то, для чего это.

Если эта карта передана вам, и вы не можете определить тип, то вы можете сделать следующее:

SortedSet<String> keys = new TreeSet<>(map.keySet());
for (String key : keys) { 
   String value = map.get(key);
   // do something
}

Это будет проходить по карте в естественном порядке ключей.


Более длинный ответ

Технически, вы можете использовать все, что реализует SortedMap, но, за исключением редких случаев, это равнозначно тому TreeMap, как Mapобычно используют реализацию HashMap.

Для случаев , когда ключи представляют собой сложный тип , который не реализует Сопоставимые или вы не хотите использовать естественный порядок , то TreeMapи TreeSetесть дополнительные конструкторы , которые позволяют проходить в Comparator:

// placed inline for the demonstration, but doesn't have to be a lambda expression
Comparator<Foo> comparator = (Foo o1, Foo o2) -> {
        ...
    }

SortedSet<Foo> keys = new TreeSet<>(comparator);
keys.addAll(map.keySet());

Помните, что при использовании TreeMapили TreeSetон будет иметь другие характеристики производительности, чем HashMapили HashSet. Грубо говоря, операции по нахождению или вставке элемента перейдут от O (1) к O (Log (N)) .

В действительности HashMap, переход от 1000 элементов к 10000 на самом деле не влияет на ваше время поиска элемента, но TreeMapвремя поиска будет примерно в 3 раза медленнее (при условии Log 2 ). Перемещение от 1000 до 100 000 будет примерно в 6 раз медленнее для каждого поиска элемента.

Jherico
источник
Я пытаюсь использовать Treemap и сортировать ключи String по длине. Я обнаружил, что получаю противоречивые результаты поиска. Видимо потому, что TreeMap считает результат сравнения 0 равным? Не уверен, как использовать это в этом случае.
Marc
1
Сравнивать 0 с результатом 0 будет равно. Если вы пишете компаратор, который сортирует по длине строки, вам нужно вернуть положительное или отрицательное значение, в зависимости от того, какая строка длиннее, и вернуть только 0, если обе строки имеют одинаковую длину. если a и b являются строками, вы можете сделать это следующим образом: `return a.length () - b.length () '(или инвертировать значения, если вы хотите, чтобы они были отсортированы в другом направлении).
Джерико
Привет, ребята, если он / она хотел бы, чтобы на карте был порядок по ключам, который здесь 1,2,3,4, каков порядок следования .... почему бы нам не использовать LinkedHashSet? Мы просто ставим вопросы один за другим, и они упорядочиваются по порядку диссертации. Кто-нибудь может мне помочь с этим?
Кароли
@Karoly LinkedHashSet будет работать для извлечения элементов в том порядке, в котором вы их вставили. ОП хочет получить элементы в некотором предопределенном порядке сортировки, независимо от порядка вставки.
Дэвид Берри
@ cricket_007 код демонстрирует, как перебирать ключи для карты, которая еще не отсортирована.
Джерико
137

Предполагая, что TreeMap не подходит для вас (и что вы не можете использовать дженерики):

List sortedKeys=new ArrayList(yourMap.keySet());
Collections.sort(sortedKeys);
// Do what you need with sortedKeys.
TrayMan
источник
3
Спасибо! Мне нужно было сделать что-то подобное, так как мои ключи были сложного типа.
Росс Хамбрик
3
Это просто отсортирует список ключей, но не отсортирует саму карту по ключам. Я также смотрю, как отсортировать карту по ключам и найти способ. Мне придется попробовать с TreeMap, я думаю :)
Crenguta S
55

С помощью TreeMapвы можете отсортировать карту.

Map<String, String> map = new HashMap<>();        
Map<String, String> treeMap = new TreeMap<>(map);
for (String str : treeMap.keySet()) {
    System.out.println(str);
}
Манодж Сингх
источник
1
Карта <String, List <String >> treeMap = new TreeMap <String, List <String >> (printHashMap); for (String str: treeMap.keySet ()) {System.out.println (str + "" + treeMap.get (str)); }
vikramvi
37

Используйте TreeMap !

AgileJon
источник
31
+1 - недостаточно ловкий, чтобы победить Джерико, Джон, но все равно довольно хорошо. 8)
duffymo
Я не знаю Java :-( Это работает на 100%.
Гораздо
36

Если у вас уже есть карта и вы хотите отсортировать ее по ключам, просто используйте:

Map<String, String> treeMap = new TreeMap<String, String>(yourMap);

Полный рабочий пример:

import java.util.HashMap;
import java.util.Set;
import java.util.Map;
import java.util.TreeMap;
import java.util.Iterator;

class SortOnKey {

public static void main(String[] args) {
   HashMap<String,String> hm = new HashMap<String,String>();
   hm.put("3","three");
   hm.put("1","one");
   hm.put("4","four");
   hm.put("2","two");
   printMap(hm);
   Map<String, String> treeMap = new TreeMap<String, String>(hm);
   printMap(treeMap);
}//main

public static void printMap(Map<String,String> map) {
    Set s = map.entrySet();
    Iterator it = s.iterator();
    while ( it.hasNext() ) {
       Map.Entry entry = (Map.Entry) it.next();
       String key = (String) entry.getKey();
       String value = (String) entry.getValue();
       System.out.println(key + " => " + value);
    }//while
    System.out.println("========================");
}//printMap

}//class
Мэриленд
источник
36

Просто используйте TreeMap

new TreeMap<String, String>(unsortMap);

Имейте в виду, что TreeMap сортируется в соответствии с естественным порядком его «ключей»

Aliti
источник
19

При условии, что вы не можете использовать TreeMap, в Java 8 мы можем использовать метод toMap (),Collectors который принимает следующие параметры:

  • keymapper : функция отображения для создания ключей
  • valuemapper : функция отображения для получения значений
  • mergeFunction : функция слияния, используемая для разрешения коллизий между значениями, связанными с одним и тем же ключом
  • mapSupplier : функция, которая возвращает новую пустую карту, в которую будут вставлены результаты.

Пример Java 8

Map<String,String> sample = new HashMap<>();  // push some values to map  
Map<String, String> newMapSortedByKey = sample.entrySet().stream()
                    .sorted(Map.Entry.<String,String>comparingByKey().reversed())
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
Map<String, String> newMapSortedByValue = sample.entrySet().stream()
                        .sorted(Map.Entry.<String,String>comparingByValue().reversed())
                        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1,e2) -> e1, LinkedHashMap::new));

Мы можем изменить пример для использования собственного компаратора и сортировки по ключам следующим образом:

Map<String, String> newMapSortedByKey = sample.entrySet().stream()
                .sorted((e1,e2) -> e1.getKey().compareTo(e2.getKey()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1,e2) -> e1, LinkedHashMap::new));
akhil_mittal
источник
работает только в Android с Api 24 и выше.
Тина
10

Используя Java 8:

Map<String, Integer> sortedMap = unsortMap.entrySet().stream()
            .sorted(Map.Entry.comparingByKey())
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                    (oldValue, newValue) -> oldValue, LinkedHashMap::new));
Тарас Мельник
источник
5

Этот код может сортировать карту значения ключа в обоих порядках, то есть по возрастанию и по убыванию.

<K, V extends Comparable<V>> Map<K, V> sortByValues
     (final Map<K, V> map, int ascending)
{
     Comparator<K> valueComparator =  new Comparator<K>() {         
        private int ascending;
        public int compare(K k1, K k2) {
            int compare = map.get(k2).compareTo(map.get(k1));
            if (compare == 0) return 1;
            else return ascending*compare;
        }
        public Comparator<K> setParam(int ascending)
        {
            this.ascending = ascending;
            return this;
        }
    }.setParam(ascending);

    Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
    sortedByValues.putAll(map);
    return sortedByValues;
}

Например:

Map<Integer,Double> recommWarrVals = new HashMap<Integer,Double>();
recommWarrVals = sortByValues(recommWarrVals, 1);  // Ascending order
recommWarrVals = sortByValues(recommWarrVals,-1);  // Descending order
М. Машай
источник
5

В Java 8

Чтобы отсортировать Map<K, V>по ключу, поместите ключи в List<K>:

List<K> result = map.keySet().stream().sorted().collect(Collectors.toList());

Чтобы отсортировать Map<K, V>по ключу, поместите записи в List<Map.Entry<K, V>>:

List<Map.Entry<K, V>> result =
    map.entrySet()
       .stream()
       .sorted(Map.Entry.comparingByKey())
       .collect(Collectors.toList());

И последнее, но не менее важное: сортировка строк с учетом языка - используйте класс Collator (компаратор):

Collator collator = Collator.getInstance(Locale.US);
collator.setStrength(Collator.PRIMARY); // case insensitive collator

List<Map.Entry<String, String>> result =
    map.entrySet()
       .stream()
       .sorted(Map.Entry.comparingByKey(collator))
       .collect(Collectors.toList());
Александр Пирохов
источник
1
List<String> list = new ArrayList<String>();
Map<String, String> map = new HashMap<String, String>();
for (String str : map.keySet()) {
 list.add(str);
}
Collections.sort(list);
for (String str : list) {
 System.out.println(str);
}
Манодж Сингх
источник
1

В Java 8 вы также можете использовать .stream (). Sorted ():

myMap.keySet().stream().sorted().forEach(key -> {
        String value = myMap.get(key);

        System.out.println("key: " + key);
        System.out.println("value: " + value);
    }
);
Jonas_Hess
источник
0

Мы также можем отсортировать ключ с помощью метода Arrays.sort.

Map<String, String> map = new HashMap<String, String>();
Object[] objArr = new Object[map.size()];
for (int i = 0; i < map.size(); i++) {
objArr[i] = map.get(i);
}
Arrays.sort(objArr);
for (Object str : objArr) {
System.out.println(str);
}
Манодж Сингх
источник
Это сортировка по значению, а не по ключу.
Raaj
0

На всякий случай, если вы не хотите использовать TreeMap

public static Map<Integer, Integer> sortByKey(Map<Integer, Integer> map) {
    List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
    list.sort(Comparator.comparingInt(Map.Entry::getKey));
    Map<Integer, Integer> sortedMap = new HashMap<>();
    list.forEach(sortedMap.put(e.getKey(), e.getValue()));
    return sortedMap;
}

Кроме того, в случае, если вы хотите отсортировать карту на основе valuesпросто изменить Map.Entry::getKeyнаMap.Entry::getValue

Анкит Шарма
источник
Это появилось в моем журнале обзора. Кто-то пытался исправить некоторые ошибки с именами переменных (но не получилось). Итак, я исправил их правильно, однако этот ответ в корне неверен. Порядок, в котором вы добавляете значения в HashMap, не имеет значения. Карты не имеют порядка. stackoverflow.com/questions/10710193/...
Aepryus