Làm cách nào để sắp xếp các giá trị Bản đồ theo khóa trong Java?


362

Tôi có một Bản đồ có các chuỗi cho cả khóa và giá trị.

Dữ liệu như sau:

"câu hỏi 1", "1"
"câu hỏi 9", "1"
"câu hỏi 2", "4"
"câu hỏi 5", "2"

Tôi muốn sắp xếp bản đồ dựa trên các phím của nó. Vì vậy, cuối cùng, tôi sẽ có question1, question2, question3.... và như vậy.


Cuối cùng, tôi đang cố gắng đưa hai chuỗi ra khỏi Bản đồ này.

  • Chuỗi đầu tiên: Câu hỏi (theo thứ tự 1 ..10)
  • Chuỗi thứ hai: Đáp án (theo thứ tự giống như câu hỏi)

Ngay bây giờ tôi có những điều sau đây:

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

Điều này cho tôi các câu hỏi trong một chuỗi nhưng chúng không theo thứ tự.

Câu trả lời:


613

Câu trả lời ngắn

Sử dụng a TreeMap. Đây chính xác là những gì nó làm.

Nếu bản đồ này được chuyển cho bạn và bạn không thể xác định loại, thì bạn có thể làm như sau:

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

Điều này sẽ lặp đi lặp lại trên bản đồ theo thứ tự tự nhiên của các phím.


Câu trả lời dài hơn

Về mặt kỹ thuật, bạn có thể sử dụng bất cứ thứ gì thực hiện SortedMap, nhưng ngoại trừ những trường hợp hiếm hoi, số tiền này tương đương với TreeMapviệc sử dụng một Maptriển khai thường lên tới HashMap.

Đối với trường hợp khóa của bạn là loại phức tạp không triển khai Có thể so sánh hoặc bạn không muốn sử dụng thứ tự tự nhiên sau đó TreeMapTreeSetcó các hàm tạo bổ sung cho phép bạn chuyển qua 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());

Hãy nhớ khi sử dụng một TreeMaphoặc TreeSetnó sẽ có các đặc tính hiệu suất khác với HashMaphoặc HashSet. Nói một cách đơn giản là các hoạt động tìm hoặc chèn một phần tử sẽ đi từ O (1) đến O (Nhật ký (N)) .

Trong một HashMap, việc chuyển từ 1000 mục thành 10.000 không thực sự ảnh hưởng đến thời gian của bạn để tra cứu một yếu tố, nhưng trong TreeMapthời gian tra cứu sẽ chậm hơn khoảng 3 lần (giả sử Nhật ký 2 ). Di chuyển từ 1000 đến 100.000 sẽ chậm hơn khoảng 6 lần cho mỗi lần tra cứu phần tử.


Tôi đang cố gắng sử dụng Treemap và sắp xếp các khóa Chuỗi dựa trên độ dài. Tôi thấy tôi đang nhận được kết quả truy xuất không nhất quán. Rõ ràng bởi vì TreeMap coi kết quả so sánh bằng 0 là "bằng"? Không chắc chắn làm thế nào để sử dụng nó trong trường hợp này.
Marc

1
so sánh kết quả () của 0 là 'bằng'. Nếu bạn đang viết một bộ so sánh sắp xếp theo độ dài chuỗi, thì bạn cần trả về giá trị dương hoặc âm dựa trên chuỗi nào dài hơn và chỉ trả về 0 nếu cả hai chuỗi có cùng độ dài. nếu a và b là các chuỗi bạn có thể làm điều này giống như vậy `return a.length () - b.length () '(hoặc đảo ngược các giá trị nếu bạn muốn chúng được sắp xếp theo hướng khác).
Jherico

Xin chào các bạn, nếu anh ấy / cô ấy đến Bản đồ để được sắp xếp theo khóa, ở đây là 1,2,3,4, thứ tự chèn là gì .... tại sao chúng ta không sử dụng LinkedHashset? Chúng tôi chỉ đặt từng câu hỏi một và nó được sắp xếp theo thứ tự chèn. Một số có thể giúp tôi ra với điều này?
Karoly

@Karoly LinkedHashSet sẽ hoạt động để truy xuất các phần tử theo thứ tự bạn đã chèn chúng. Những gì OP muốn là lấy các phần tử theo một thứ tự sắp xếp được xác định trước, bất kể thứ tự chèn.
David Berry

@ cricket_007 mã đang trình bày cụ thể cách lặp qua các khóa của chúng cho bản đồ chưa được sắp xếp.
Jherico

137

Giả sử TreeMap không tốt cho bạn (và giả sử bạn không thể sử dụng thuốc generic):

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

3
Cảm ơn! Tôi cần phải làm một cái gì đó như thế này vì các phím của tôi là một loại phức tạp.
Ross Hambrick

3
Điều này sẽ chỉ sắp xếp danh sách các khóa, nhưng sẽ không tự sắp xếp bản đồ dựa trên các khóa. Tôi cũng đang tìm cách sắp xếp bản đồ dựa trên các phím và có thể tìm ra cách. Tôi sẽ phải thử với TreeMap tôi đoán :)
Crenguta S

55

Sử dụng TreeMapbạn có thể sắp xếp bản đồ.

Map<String, String> map = new HashMap<>();        
Map<String, String> treeMap = new TreeMap<>(map);
for (String str : treeMap.keySet()) {
    System.out.println(str);
}

1
Bản đồ <Chuỗi, Danh sách <Chuỗi >> treeMap = new TreeMap <Chuỗi, Danh sách <Chuỗi >> (printHashMap); for (String str: treeMap.keySet ()) {System.out.println (str + "" + treeMap.get (str)); }
vikramvi

37

Sử dụng một TreeMap !


31
+1 - Không đủ nhanh nhẹn để đánh bại Jherico, Jon, nhưng vẫn khá tốt. 8)
duffymo

Tôi không biết Java :-( Cách này hiệu quả 100%. Đơn giản hơn nhiều so với bất kỳ giải pháp khủng khiếp nào tôi nghĩ ra
peterchaula

36

Nếu bạn đã có bản đồ và muốn sắp xếp nó trên các phím, chỉ cần sử dụng:

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

Một ví dụ hoạt động hoàn chỉnh:

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

Chỉ cần sử dụng TreeMap

new TreeMap<String, String>(unsortMap);

Xin lưu ý rằng TreeMap được sắp xếp theo thứ tự tự nhiên của các 'khóa' của nó


19

Với điều kiện bạn không thể sử dụng TreeMap, trong Java 8, chúng ta có thể sử dụng phương thức toMap () trong Collectorsđó có các tham số sau:

  • keymapper : chức năng ánh xạ để tạo khóa
  • valuemapper : chức năng ánh xạ để tạo ra các giá trị
  • mergeFunction : một hàm hợp nhất, được sử dụng để giải quyết xung đột giữa các giá trị được liên kết với cùng một khóa
  • mapSupplier : một hàm trả về một Bản đồ mới, trống, trong đó các kết quả sẽ được chèn vào.

Ví dụ 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));

Chúng ta có thể sửa đổi ví dụ để sử dụng bộ so sánh tùy chỉnh và sắp xếp dựa trên các khóa như:

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

chỉ hoạt động trong Android với Api 24 trở lên.
Tina

10

Sử dụng 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

Mã này có thể sắp xếp bản đồ khóa-giá trị theo cả hai đơn hàng, tức là tăng dần và giảm dần.

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

Ví dụ:

Map<Integer,Double> recommWarrVals = new HashMap<Integer,Double>();
recommWarrVals = sortByValues(recommWarrVals, 1);  // Ascending order
recommWarrVals = sortByValues(recommWarrVals,-1);  // Descending order

5

Trong Java 8

Để sắp xếp một Map<K, V>phím, đặt các phím vào List<K>:

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

Để sắp xếp Map<K, V>theo khóa, đặt các mục vào List<Map.Entry<K, V>>:

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

Cuối cùng nhưng không kém phần quan trọng: để sắp xếp các chuỗi theo cách nhạy cảm với miền địa phương - sử dụng lớp Collator (bộ so sánh):

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

Trong Java 8, bạn cũng có thể sử dụng .stream (). Sort ():

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

        System.out.println("key: " + key);
        System.out.println("value: " + value);
    }
);

0

Chúng ta cũng có thể sắp xếp khóa bằng cách sử dụng phương thức 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);
}

Đây là sắp xếp theo giá trị và không quan trọng.
raaj

0

Chỉ trong trường hợp bạn không muốn sử dụng 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;
}

Ngoài ra, trong trường hợp bạn muốn sắp xếp bản đồ của mình trên cơ sở valueschỉ cần thay đổi Map.Entry::getKeythànhMap.Entry::getValue


Điều này xuất hiện trong nhật ký đánh giá của tôi. Ai đó đã cố gắng sửa một số lỗi với tên biến (nhưng không thành công). Vì vậy, tôi đã sửa chúng một cách chính xác, tuy nhiên, câu trả lời này chỉ sai về cơ bản. Thứ tự bạn thêm giá trị vào HashMap là không liên quan. Bản đồ không có thứ tự. stackoverflow.com/questions/10710193/ cường
aepryus
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.