Як сортувати значення карт за ключовими словами на Java?


361

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

Дані виглядають так:

"питання1", "1"
"питання9", "1"
"питання2", "4"
"питання5", "2"

Я хочу сортувати карту за її ключами. Отже, врешті-решт, у мене буде question1, question2, question3.... і так далі.


Врешті-решт, я намагаюся витягнути два рядки з цієї карти.

  • Перший рядок: Запитання (в порядку 1 ..10)
  • Другий рядок: відповіді (у тому ж порядку, що і питання)

Зараз у мене є наступне:

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

Це задає мені запитання в рядку, але вони не в порядку.

Відповіді:


612

Коротка відповідь

Використовуйте a 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 елементів до 10 000 насправді не впливає на ваш час на пошук елемента, але TreeMapчас пошуку буде приблизно в 3 рази повільнішим (якщо припустити журнал 2 ). Переміщення від 1000 до 100000 буде приблизно в 6 разів повільніше для кожного пошуку елементів.


Я намагаюся використовувати Treemap і сортувати String-клавіші залежно від довжини. Я вважаю, що отримую суперечливі результати пошуку. Мабуть тому, що TreeMap вважає результат порівнянняДо результату 0 "рівним"? Не знаєте, як його використовувати в цьому випадку.
Марк

1
порівняти до () результат 0 дорівнює "дорівнює". Якщо ви пишете компаратор, який сортує за довжиною рядка, вам потрібно повернути додатне або від’ємне значення, залежно від того, яка строка довша, і повернути лише 0, якщо обидва рядки однакової довжини. якщо a і b - це рядки, ви можете зробити це так, як `return a.length () - b.length () '(або змінити значення, якщо ви хочете, щоб вони були відсортовані в іншому напрямку).
Jherico

Привіт, хлопці, якщо він / вона хотів би замовити карту за ключами, яких тут 1,2,3,4, який порядок вставки .... чому б ми не використовували LinkedHashSet? Ми просто ставимо питання по черзі, і це впорядковується за порядком вставки. Чи можу я допомогти мені в цьому?
Каролі

@Karoly LinkedHashSet працює над тим, щоб відновити елементи у порядку, який ви вставили. Оперативна програма бажає отримати елементи у визначеному порядку сортування, незалежно від порядку вставки.
Девід Беррі

@ cricket_007 код демонструє конкретно, як перебирати клавіші для карти, яка ще не сортована.
Jherico

137

Якщо припустити, що TreeMap не корисний для вас (і якщо ви не можете використовувати дженерики):

List sortedKeys=new ArrayList(yourMap.keySet());
Collections.sort(sortedKeys);
// Do what you need with sortedKeys.

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 !


31
+1 - недостатньо спритний, щоб перемогти Джереко, Джона, але все ще досить добре. 8)
duffymo

Я не знаю Java :-( Це працює на 100%. Набагато простіше, ніж будь-яке жахливе рішення, яке я придумав
peterchaula

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 сортується відповідно до природного впорядкування його «клавіш»


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

працює лише в Android з Api 24 і новішими версіями.
Тіна

9

Використання 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);
    }
);

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.