จัดเรียงแผนที่ <คีย์ค่า> ตามค่า


1634

ฉันค่อนข้างใหม่กับ Java และมักพบว่าฉันต้องเรียงลำดับMap<Key, Value>ค่า

เนื่องจากค่านั้นไม่ซ้ำกันฉันจึงพบว่าฉันแปลงค่าkeySetเป็นarrayและเรียงลำดับอาร์เรย์นั้นผ่านการจัดเรียงอาร์เรย์ด้วยตัวเปรียบเทียบแบบกำหนดเองที่เรียงลำดับตามค่าที่เกี่ยวข้องกับคีย์

มีวิธีที่ง่ายกว่านี้ไหม?


24
แผนที่ไม่ได้ถูกจัดเรียง แต่สามารถเข้าถึงได้อย่างรวดเร็ว ค่าเท่ากับวัตถุทำลายข้อ จำกัด ของแผนที่ ใช้ชุดรายการเช่นList<Map.Entry<...>> list =new LinkedList(map.entrySet())และCollections.sort ....เป็นอย่างนั้น
Hannes

1
กรณีที่อาจเกิดขึ้นเมื่อเราพยายามใช้ Counter ใน Java (Map <Object, Integer>) การเรียงลำดับตามจำนวนครั้งที่เกิดขึ้นจะเป็นการดำเนินการทั่วไป ภาษาเช่น Python มีโครงสร้างข้อมูล Counter สำหรับวิธีอื่นในการนำไปใช้ใน Java นี่คือตัวอย่าง
demongolem

6
มีกรณีการใช้งานมากมายสำหรับแผนที่ที่เรียงลำดับนั่นคือเหตุผลที่คุณมี TreeMap และ ConcurrentSkipListMap ใน jdk
alobodzk


1
TreeMap และ ConcurrentSkipListMap เรียงลำดับตามคีย์ คำถามเกี่ยวกับการจัดเรียงตามค่า
ปีเตอร์

คำตอบ:


899

นี่เป็นเวอร์ชั่นที่ง่ายต่อการใช้งาน:

public class MapUtil {
    public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
        List<Entry<K, V>> list = new ArrayList<>(map.entrySet());
        list.sort(Entry.comparingByValue());

        Map<K, V> result = new LinkedHashMap<>();
        for (Entry<K, V> entry : list) {
            result.put(entry.getKey(), entry.getValue());
        }

        return result;
    }
}

10
ดีใจที่มันช่วย จอห์น LinkedHashMap นั้นมีความสำคัญต่อโซลูชันเนื่องจากมีคำสั่งการวนซ้ำที่คาดการณ์ได้
คาร์เตอร์หน้า

3
@ buzz3791 จริง นั่นจะเป็นกรณีในอัลกอริทึมการเรียงลำดับใด ๆ เปลี่ยนค่าของโหนดในโครงสร้างในระหว่างการเรียงลำดับสร้างผลลัพธ์ที่ไม่แน่นอน (และเกือบจะไม่ดีเสมอ) ผลลัพธ์
คาร์เตอร์หน้า

3
@heagorath ฉันลองใน Android และใช้งานได้เช่นกัน ไม่ใช่ปัญหาเฉพาะแพลตฟอร์มเนื่องจากคุณใช้ Java 6 เวอร์ชัน คุณนำไปใช้เปรียบเทียบได้อย่างถูกต้องในวัตถุค่าของคุณ?
saiyancoder

6
ไม่ควรใช้ Java 8 version forEachOrderedแทนforEachเนื่องจาก docs of forEachstates: "พฤติกรรมของการดำเนินการนี้เป็นแบบไม่ระบุชื่ออย่างชัดเจน"?
ปล้น

1
ฉีกทั้งหมดนี้ แต่ให้เครดิต @CarterPage ในความคิดเห็น (มันจะอยู่ในโครงการโอเพนซอร์สอยู่ดี) ขอบคุณมาก.
นาธานบีช

419

โน๊ตสำคัญ:

รหัสนี้สามารถแตกได้หลายวิธี หากคุณตั้งใจจะใช้รหัสที่ให้มาโปรดแน่ใจว่าได้อ่านความคิดเห็นและรับทราบถึงความเกี่ยวข้อง ตัวอย่างเช่นคีย์ไม่สามารถดึงค่าได้อีกต่อไป ( getส่งคืนเสมอnull)


ดูเหมือนง่ายกว่าที่กล่าวมาทั้งหมด ใช้ TreeMap ดังต่อไปนี้:

public class Testing {
    public static void main(String[] args) {
        HashMap<String, Double> map = new HashMap<String, Double>();
        ValueComparator bvc = new ValueComparator(map);
        TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc);

        map.put("A", 99.5);
        map.put("B", 67.4);
        map.put("C", 67.4);
        map.put("D", 67.3);

        System.out.println("unsorted map: " + map);
        sorted_map.putAll(map);
        System.out.println("results: " + sorted_map);
    }
}

class ValueComparator implements Comparator<String> {
    Map<String, Double> base;

    public ValueComparator(Map<String, Double> base) {
        this.base = base;
    }

    // Note: this comparator imposes orderings that are inconsistent with
    // equals.
    public int compare(String a, String b) {
        if (base.get(a) >= base.get(b)) {
            return -1;
        } else {
            return 1;
        } // returning 0 would merge keys
    }
}

เอาท์พุท:

unsorted map: {D=67.3, A=99.5, B=67.4, C=67.4}
results: {D=67.3, B=67.4, C=67.4, A=99.5}

18
ไม่อีกแล้ว ( stackoverflow.com/questions/109383/… ) นอกจากนี้ทำไมมีนักแสดงถึง Double? มันไม่ควรจะเป็นเพียงแค่return ((Comparable)base.get(a).compareTo(((Comparable)base.get(b)))?
สตีเฟน

12
@Stephen: ไม่ในกรณีนี้คีย์ทั้งหมดที่เท่ากับตามค่าจะถูกดร็อป (ความแตกต่างระหว่างเท่ากับและเปรียบเทียบโดยการอ้างอิง) นอกจากนี้: แม้แต่รหัสนี้ก็มีปัญหากับลำดับต่อไปนี้ map.put("A","1d");map.put("B","1d");map.put("C",67d);map.put("D",99.5d);
steffen

43
ตัวเปรียบเทียบที่ใช้สำหรับ treemap ไม่สอดคล้องกับเท่ากับ (ดู sortMap javadox) นี่หมายถึงการเลิกใช้รายการจากแผนผังต้นไม้จะไม่ทำงาน Sort_map.get ("A") จะคืนค่าว่าง นั่นหมายความว่าการใช้ treemap นี้หัก
mR_fr0g

87
ในกรณีที่มันไม่ชัดเจนสำหรับคน: วิธีนี้อาจจะไม่ทำสิ่งที่คุณต้องการถ้าคุณมีการทำแผนที่หลายคีย์เป็นค่าเดียวกัน - เพียงหนึ่งในกุญแจเหล่านั้นจะปรากฏในผลการเรียงลำดับ
Maxy-B

63
Louis Wasserman (ใช่หนึ่งใน Google Guava) ไม่ชอบคำตอบนี้: "มันแบ่งออกเป็นหลาย ๆ วิธีที่สับสนถ้าคุณดูตลกถ้าเปลี่ยนแผนที่สำรองมันจะแตกถ้ามีหลายปุ่ม map เป็นค่าเดียวกันมันจะแตกถ้าคุณโทรหาคีย์ที่ไม่ได้อยู่ในแผนที่สำรองมันจะแตกถ้าคุณทำสิ่งใดก็ตามที่จะทำให้การค้นหาเกิดขึ้นกับคีย์ที่ไม่ได้อยู่ใน แผนที่ - การเรียก Map.equals ประกอบด้วยKeyอะไรก็ได้ - มันจะแตกด้วยร่องรอยสแต็กแปลก ๆ " plus.google.com/102216152814616302326/posts/bEQLDK712MJ
haylem

339

Java 8 เสนอคำตอบใหม่: แปลงรายการเป็นสตรีมและใช้ combatorators เปรียบเทียบจาก Map.Entry:

Stream<Map.Entry<K,V>> sorted =
    map.entrySet().stream()
       .sorted(Map.Entry.comparingByValue());

สิ่งนี้จะช่วยให้คุณใช้รายการที่เรียงตามลำดับจากน้อยไปหามาก หากคุณต้องการหาค่าจากมากไปน้อยเพียงแค่ทำการเปรียบเทียบ:

Stream<Map.Entry<K,V>> sorted =
    map.entrySet().stream()
       .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));

หากค่านั้นไม่สามารถเทียบเคียงกันได้คุณสามารถผ่านตัวเปรียบเทียบที่ชัดเจนได้:

Stream<Map.Entry<K,V>> sorted =
    map.entrySet().stream()
       .sorted(Map.Entry.comparingByValue(comparator));

จากนั้นคุณสามารถใช้การดำเนินการสตรีมอื่น ๆ เพื่อใช้ข้อมูลต่อไป ตัวอย่างเช่นหากคุณต้องการ 10 อันดับแรกในแผนที่ใหม่:

Map<K,V> topTen =
    map.entrySet().stream()
       .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
       .limit(10)
       .collect(Collectors.toMap(
          Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

หรือพิมพ์ไปที่System.out:

map.entrySet().stream()
   .sorted(Map.Entry.comparingByValue())
   .forEach(System.out::println);

ดี แต่สิ่งที่เกี่ยวกับการใช้งานparallelStream()ในกรณีนี้?
Benj

11
มันจะทำงานในแบบคู่ขนานอย่างไรก็ตามคุณอาจพบว่าค่าใช้จ่ายในการรวมแผนที่เพื่อรวมผลลัพธ์บางส่วนนั้นแพงเกินไปและเวอร์ชันแบบขนานอาจทำงานได้ไม่ดีเท่าที่คุณต้องการ แต่มันทำงานและสร้างคำตอบที่ถูกต้อง
Brian Goetz

ขอบคุณสำหรับคำแนะนำที่เป็นประโยชน์ของคุณ มันเป็นสิ่งที่ฉันสงสัยแม้ว่ามันจะขึ้นอยู่กับประเภทของคีย์ที่คุณใช้และพารามิเตอร์มากมาย ... สิ่งที่สำคัญคือ "มันทำงานและสร้างคำตอบที่ถูกต้อง"
Benj

2
คุณไม่จำเป็นต้องใช้ comparByValue ในตัวอย่าง top10 หรือไม่?
ลีโอ

1
@Benj มันจะทำงานในแง่ของการแยก 10 อันดับแรก แต่แผนที่ผลลัพธ์จะไม่ถูกสั่งอีกต่อไป
OrangeDog

211

คำตอบ 1 บรรทัดสามคำ ...

ฉันจะใช้Google Collections Guavaเพื่อทำสิ่งนี้ - ถ้าคุณมีค่าComparableคุณก็สามารถใช้ได้

valueComparator = Ordering.natural().onResultOf(Functions.forMap(map))

ซึ่งจะสร้างฟังก์ชั่น (วัตถุ) สำหรับแผนที่ [ที่ใช้คีย์ใด ๆ เป็นอินพุตป้อนคืนค่าที่เกี่ยวข้อง] จากนั้นใช้การเรียงลำดับตามธรรมชาติ (เทียบเคียง) กับพวกเขา [ค่า]

หากพวกเขาไม่เทียบเท่าคุณจะต้องทำอะไรบางอย่างตามแนวของ

valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map)) 

สิ่งเหล่านี้อาจนำไปใช้กับ TreeMap (ตามที่OrderingขยายComparator) หรือLinkedHashMap หลังจากการเรียงลำดับบางอย่าง

หมายเหตุ : หากคุณจะใช้ TreeMap โปรดจำไว้ว่าหากมีการเปรียบเทียบ == 0 รายการนั้นจะอยู่ในรายการแล้ว (ซึ่งจะเกิดขึ้นหากคุณมีหลายค่าที่เปรียบเทียบเหมือนกัน) ในการบรรเทาปัญหานี้คุณสามารถเพิ่มคีย์ของคุณลงในเครื่องมือเปรียบเทียบได้ (สมมติว่าคีย์และค่าของคุณเป็นComparableดังนี้):

valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())

= ใช้การเรียงแบบเป็นธรรมชาติกับค่าที่แมปด้วยคีย์และสารประกอบที่มีการเรียงลำดับตามธรรมชาติของคีย์

หมายเหตุว่านี้จะยังคงไม่ทำงานถ้าคีย์ของคุณเปรียบเทียบกับ 0 แต่เรื่องนี้ควรจะเพียงพอสำหรับส่วนมากcomparableรายการ (ตามhashCode, equalsและcompareToมักจะอยู่ในซิงค์ ... )

ดูOrdering.onResultOf ()และFunctions.forMap ()

การดำเนินงาน

ดังนั้นตอนนี้เรามีผู้เปรียบเทียบที่ทำสิ่งที่เราต้องการเราต้องได้รับผลจากมัน

map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);

ตอนนี้สิ่งนี้จะทำงานได้ แต่:

  1. จำเป็นต้องทำตามแผนที่เสร็จสมบูรณ์
  2. อย่าพยายาม comparators ดังกล่าวข้างต้นที่TreeMap; ไม่มีจุดใดที่พยายามเปรียบเทียบคีย์ที่ถูกแทรกเมื่อมันไม่มีค่าจนกระทั่งหลังจากที่ใส่นั่นคือมันจะแตกเร็วมาก

จุดที่ 1 เป็นข้อตกลงที่ดีสำหรับฉัน คอลเลคชั่น google ขี้เกียจอย่างไม่น่าเชื่อ (ซึ่งก็ดี: คุณสามารถทำทุกอย่างได้อย่างรวดเร็วในทันทีการทำงานจริงจะเกิดขึ้นเมื่อคุณเริ่มใช้ผลลัพธ์) และต้องคัดลอกแผนที่ทั้งหมด !

คำตอบ "เต็ม" / แผนที่เรียงสดตามค่า

อย่ากังวลไป หากคุณหมกมุ่นอยู่กับการมีแผนที่ "สด" เรียงลำดับในลักษณะนี้คุณสามารถแก้ปัญหาไม่ได้ทั้งคู่ (!) จากปัญหาด้านบนด้วยสิ่งที่บ้าคลั่งดังต่อไปนี้:

หมายเหตุ: สิ่งนี้มีการเปลี่ยนแปลงอย่างมากในเดือนมิถุนายน 2012 - รหัสก่อนหน้านี้ไม่สามารถใช้งานได้: ต้องใช้ HashMap ภายในเพื่อค้นหาค่าโดยไม่ต้องสร้างวงวนไม่สิ้นสุดระหว่างTreeMap.get()-> compare()และcompare()->get()

import static org.junit.Assert.assertEquals;

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

import com.google.common.base.Functions;
import com.google.common.collect.Ordering;

class ValueComparableMap<K extends Comparable<K>,V> extends TreeMap<K,V> {
    //A map for doing lookups on the keys for comparison so we don't get infinite loops
    private final Map<K, V> valueMap;

    ValueComparableMap(final Ordering<? super V> partialValueOrdering) {
        this(partialValueOrdering, new HashMap<K,V>());
    }

    private ValueComparableMap(Ordering<? super V> partialValueOrdering,
            HashMap<K, V> valueMap) {
        super(partialValueOrdering //Apply the value ordering
                .onResultOf(Functions.forMap(valueMap)) //On the result of getting the value for the key from the map
                .compound(Ordering.natural())); //as well as ensuring that the keys don't get clobbered
        this.valueMap = valueMap;
    }

    public V put(K k, V v) {
        if (valueMap.containsKey(k)){
            //remove the key in the sorted set before adding the key again
            remove(k);
        }
        valueMap.put(k,v); //To get "real" unsorted values for the comparator
        return super.put(k, v); //Put it in value order
    }

    public static void main(String[] args){
        TreeMap<String, Integer> map = new ValueComparableMap<String, Integer>(Ordering.natural());
        map.put("a", 5);
        map.put("b", 1);
        map.put("c", 3);
        assertEquals("b",map.firstKey());
        assertEquals("a",map.lastKey());
        map.put("d",0);
        assertEquals("d",map.firstKey());
        //ensure it's still a map (by overwriting a key, but with a new value) 
        map.put("d", 2);
        assertEquals("b", map.firstKey());
        //Ensure multiple values do not clobber keys
        map.put("e", 2);
        assertEquals(5, map.size());
        assertEquals(2, (int) map.get("e"));
        assertEquals(2, (int) map.get("d"));
    }
 }

เมื่อเราใส่เรามั่นใจว่าแผนที่แฮชมีค่าสำหรับตัวเปรียบเทียบแล้วใส่ลงใน TreeSet สำหรับการเรียงลำดับ แต่ก่อนหน้านั้นเราตรวจสอบแผนที่แฮชเพื่อดูว่ารหัสนั้นไม่ได้ซ้ำกันจริง ๆ นอกจากนี้ตัวเปรียบเทียบที่เราสร้างจะรวมคีย์ไว้ด้วยเพื่อที่ค่าที่ซ้ำกันจะไม่ลบคีย์ที่ไม่ซ้ำกัน (เนื่องจากการเปรียบเทียบ ==) 2 รายการเหล่านี้มีความสำคัญในการรับรองสัญญาแผนที่ หากคุณคิดว่าคุณไม่ต้องการสิ่งนั้นแสดงว่าคุณเกือบถึงจุดที่จะกลับแผนที่ทั้งหมด (เป็นMap<V,K>)

ตัวสร้างจะต้องถูกเรียกว่าเป็น

 new ValueComparableMap(Ordering.natural());
 //or
 new ValueComparableMap(Ordering.from(comparator));

สวัสดี @ สตีเฟ่นคุณสามารถยกตัวอย่างวิธีการใช้การสั่งซื้อได้ไหม ฉันมองไปที่ซอร์สโค้ดของ Ordering และไม่สามารถเข้าใจได้ว่าอะไรคือ. natural (). onResultOf (... ) กลับมา! ซอร์สโค้ดคือ "public <F> การสั่งซื้อ <F> onResultOf" ฉันยังไม่รู้ว่ามันคอมไพล์ได้ยังไง! สำคัญที่สุดวิธีใช้ "<F> การสั่งซื้อ <F>" เพื่อเรียงลำดับแผนที่ มันเป็นตัวเปรียบเทียบหรือบางอย่าง? ขอบคุณ
smallufo

OrderingComparatorเป็นเพียงที่อุดมไปด้วย ฉันได้ลองแสดงความคิดเห็นแต่ละตัวอย่าง (ตัวเอียงใต้แต่ละอัน) "ธรรมชาติ" บ่งชี้ว่าวัตถุคือComparable; มันเหมือนกับ apache common's ComparableComparator onResultOfใช้ฟังก์ชันกับรายการที่กำลังเปรียบเทียบ ดังนั้นหากคุณมีฟังก์ชั่นที่เพิ่ม 1 เข้ากับจำนวนเต็มดังนั้นnatural().onResultOf(add1Function).compare(1,2)ท้ายที่สุดก็จะทำเช่นนั้น2.compareTo(3)
Stephen

ImmutableSortedMap.copyOf จะส่ง IllegalArgumentException หากมีค่าที่ซ้ำกันในแผนที่เดิม
lbalazscs

@Ibalazscs ใช่มันจะ - คุณควรจะสามารถใช้ImmutableSetMultiMapหรือImmutableListMultiMapมีชุดของตัวแปรที่ซ้ำกัน
Stephen

1
ขอบคุณสำหรับสิ่งนี้ฉันใช้โซลูชันของคุณในโครงการเดียว ฉันคิดว่ามีปัญหาในการวาง: การทำตัวเหมือนแผนที่มันต้องคืนค่าก่อนหน้านี้ที่เกี่ยวข้องกับกุญแจถ้ามันมีอยู่ แต่ถ้าเป็นแบบนี้มันจะไม่ทำ วิธีการแก้ปัญหาที่ฉันใช้คือการคืนค่าที่ถูกลบทิ้งถ้ามี
alex

185

จากhttp://www.programmersheaven.com/download/49349/download.aspx

private static <K, V> Map<K, V> sortByValue(Map<K, V> map) {
    List<Entry<K, V>> list = new LinkedList<>(map.entrySet());
    Collections.sort(list, new Comparator<Object>() {
        @SuppressWarnings("unchecked")
        public int compare(Object o1, Object o2) {
            return ((Comparable<V>) ((Map.Entry<K, V>) (o1)).getValue()).compareTo(((Map.Entry<K, V>) (o2)).getValue());
        }
    });

    Map<K, V> result = new LinkedHashMap<>();
    for (Iterator<Entry<K, V>> it = list.iterator(); it.hasNext();) {
        Map.Entry<K, V> entry = (Map.Entry<K, V>) it.next();
        result.put(entry.getKey(), entry.getValue());
    }

    return result;
}

16
รายการที่จะเรียงเป็น "รายการใหม่ที่เชื่อมโยง" ?? Gee Thankfully Collections.sort () ถ่ายโอนรายการไปยังอาร์เรย์ก่อนเพื่อหลีกเลี่ยงข้อผิดพลาดประเภทนี้ (แต่ยังคงทิ้ง ArrayList ไปยังอาร์เรย์อย่างแม่นยำควรจะเร็วกว่าการทำแบบเดียวกันสำหรับ LinkedList)
Dimitris Andreou

ไม่สามารถแปลงจาก Iterator เป็น TernaryTree.Iterator
lisak

4
@ gg.kaspersky ฉันไม่ได้พูดว่า "มันไม่ดีในการจัดเรียง LinkedList" แต่การที่ LinkedList นั้นเป็นตัวเลือกที่ไม่ดีที่นี่โดยไม่คำนึงถึงการเรียงลำดับ มากดีกว่าที่จะใช้ ArrayList และสำหรับจุดพิเศษขนาดมันที่ตรง map.size () นอกจากนี้โปรดดูcode.google.com/p/memory-measurer/wiki/… ต้นทุนเฉลี่ยต่อองค์ประกอบใน ArrayList: 5 ไบต์ต้นทุนเฉลี่ยต่อองค์ประกอบใน LinkedList: 24 ไบต์ สำหรับ ArrayList ที่มีขนาดเท่ากันต้นทุนเฉลี่ยจะเท่ากับ 4 ไบต์ นั่นคือ LinkedList ใช้SIX คูณจำนวนหน่วยความจำที่ ArrayList ต้องการ มันเป็นเพียงแค่ขยาย
Dimitris Andreou

2
การใช้ค่าข้างต้นได้รับการเรียงลำดับจากน้อยไปมาก วิธีการเรียงจากมากไปน้อย
ram

1
แทนที่ o1 และ o2 เพื่อเรียงลำดับจากมากไปน้อย
โซฮีล

68

ด้วย Java 8 คุณสามารถใช้สตรีม APIเพื่อทำมันด้วยวิธีที่ละเอียดน้อยกว่ามาก:

Map<K, V> sortedMap = map.entrySet().stream()
                         .sorted(Entry.comparingByValue())
                         .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

วิธีการจัดเรียงในลำดับย้อนกลับ?
Vlad Holubiev

6
พบวิธีแก้ปัญหา -Collections.reverseOrder(comparing(Entry::getValue))
Vlad Holubiev

1
ฉันคิดว่าฉันเห็นการพิมพ์ผิดที่นั่น - ไม่ควรเรียก "toMap" เป็น "Collector.toMap ()" ใช่หรือไม่
Jake Stokes

1
@ JakeStokes หรือใช้การนำเข้าแบบคงที่ :-)
assylias

6
วิธีที่ดีกว่าในการจัดเรียงตามมูลค่ารายการในลำดับย้อนกลับคือ:Entry.comparingByValue(Comparator.reverseOrder())
Gediminas Rimsa

31

การเรียงลำดับคีย์จำเป็นต้องใช้ Comparator เพื่อค้นหาแต่ละค่าสำหรับแต่ละการเปรียบเทียบ โซลูชันที่ปรับขนาดได้มากขึ้นจะใช้ entrySet โดยตรงตั้งแต่นั้นค่าจะพร้อมใช้งานทันทีสำหรับการเปรียบเทียบแต่ละครั้ง (แม้ว่าฉันจะไม่ได้สำรองข้อมูลด้วยตัวเลข)

นี่เป็นสิ่งทั่วไป:

public static <K, V extends Comparable<? super V>> List<K> getKeysSortedByValue(Map<K, V> map) {
    final int size = map.size();
    final List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>(size);
    list.addAll(map.entrySet());
    final ValueComparator<V> cmp = new ValueComparator<V>();
    Collections.sort(list, cmp);
    final List<K> keys = new ArrayList<K>(size);
    for (int i = 0; i < size; i++) {
        keys.set(i, list.get(i).getKey());
    }
    return keys;
}

private static final class ValueComparator<V extends Comparable<? super V>>
                                     implements Comparator<Map.Entry<?, V>> {
    public int compare(Map.Entry<?, V> o1, Map.Entry<?, V> o2) {
        return o1.getValue().compareTo(o2.getValue());
    }
}

มีวิธีในการลดการหมุนของหน่วยความจำสำหรับโซลูชันด้านบน ArrayList แรกที่สร้างขึ้นสามารถนำกลับมาใช้ใหม่เป็นค่าส่งคืนได้ สิ่งนี้จะต้องมีการปราบปรามคำเตือนทั่วไปบางอย่าง แต่มันอาจจะคุ้มค่าสำหรับรหัสห้องสมุดที่ใช้งานได้อีกครั้ง นอกจากนี้ Comparator ไม่จำเป็นต้องถูกจัดสรรใหม่ทุกครั้งที่มีการร้องขอ

นี่คือรุ่นที่มีประสิทธิภาพมากกว่าแม้ว่าจะมีเสน่ห์น้อยกว่า:

public static <K, V extends Comparable<? super V>> List<K> getKeysSortedByValue2(Map<K, V> map) {
    final int size = map.size();
    final List reusedList = new ArrayList(size);
    final List<Map.Entry<K, V>> meView = reusedList;
    meView.addAll(map.entrySet());
    Collections.sort(meView, SINGLE);
    final List<K> keyView = reusedList;
    for (int i = 0; i < size; i++) {
        keyView.set(i, meView.get(i).getKey());
    }
    return keyView;
}

private static final Comparator SINGLE = new ValueComparator();

สุดท้ายหากคุณต้องการเข้าถึงข้อมูลที่จัดเรียงอย่างต่อเนื่อง (แทนที่จะเรียงลำดับเป็นระยะ ๆ ) คุณสามารถใช้แผนที่เพิ่มเติมได้ แจ้งให้เราทราบหากคุณต้องการรายละเอียดเพิ่มเติม ...


รุ่นที่สองสามารถรัดกุมยิ่งขึ้นถ้าคุณส่งคืนรายการ <Map.Entry <K, V >> สิ่งนี้จะทำให้ง่ายขึ้นในการวนซ้ำและรับทั้งกุญแจและค่าโดยไม่ต้องทำอะไรมากมายเข้าไปในแผนที่ นี่คือทั้งหมดที่สมมติว่าคุณตกลงกับรหัสนี้ว่าเป็นเธรดที่ไม่ปลอดภัย หากมีการแชร์แผนที่สำรองหรือรายการเรียงลำดับในสภาพแวดล้อมแบบมัลติเธรดการเดิมพันทั้งหมดจะปิด
Mike Miller

26

ห้องสมุดคอมมอนส์คอลเลกชันที่มีวิธีการแก้ปัญหาที่เรียกว่าTreeBidiMap หรือคุณอาจเข้าไปดู Google Collections API มันมีTreeMultimapซึ่งคุณสามารถใช้ได้

และถ้าคุณไม่ต้องการใช้เฟรมเวิร์กเหล่านี้ ... มันมาพร้อมกับซอร์สโค้ด


คุณไม่จำเป็นต้องใช้การรวบรวมทั่วไป Java มาพร้อมกับ java.util.TreeMap ของมันเอง
yoliho

2
ใช่ แต่ TreeMap อยู่ไกลมีความยืดหยุ่นน้อยลงเมื่อเรียงลำดับในมูลค่าส่วนหนึ่งของ mapentries
p3t0r

9
ปัญหาของ BidiMap คือมันเพิ่มข้อ จำกัด ความสัมพันธ์แบบ 1: 1 ระหว่างคีย์และค่าเพื่อทำให้ความสัมพันธ์กลับด้านไม่ได้ (เช่นทั้งคีย์และค่าต้องไม่ซ้ำกัน) ซึ่งหมายความว่าคุณไม่สามารถใช้สิ่งนี้เพื่อจัดเก็บบางสิ่งเช่นวัตถุนับจำนวนคำเนื่องจากหลาย ๆ คำจะมีจำนวนเท่ากัน
Doug

26

ฉันได้ดูคำตอบที่ให้มา แต่ส่วนใหญ่มีความซับซ้อนมากกว่าที่จำเป็นหรือลบองค์ประกอบแผนที่เมื่อมีหลายปุ่มที่มีค่าเท่ากัน

นี่เป็นวิธีแก้ปัญหาที่ฉันคิดว่าเหมาะสมกว่า:

public static <K, V extends Comparable<V>> Map<K, V> sortByValues(final Map<K, V> map) {
    Comparator<K> valueComparator =  new Comparator<K>() {
        public int compare(K k1, K k2) {
            int compare = map.get(k2).compareTo(map.get(k1));
            if (compare == 0) return 1;
            else return compare;
        }
    };
    Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
    sortedByValues.putAll(map);
    return sortedByValues;
}

โปรดทราบว่าแผนที่จะเรียงลำดับจากค่าสูงสุดถึงต่ำสุด


6
ปัญหา: หากคุณต้องการใช้แผนที่ที่ส่งคืนในภายหลังตัวอย่างเช่นเพื่อตรวจสอบว่ามีองค์ประกอบบางอย่างหรือไม่คุณจะได้รับข้อมูลเท็จเสมอเนื่องจากตัวเปรียบเทียบที่กำหนดเองของคุณ! วิธีแก้ปัญหาที่เป็นไปได้: แทนที่บรรทัดสุดท้ายด้วย: คืนค่า LinkedHashMap ใหม่ <K, V> (เรียงลำดับโดยByValues);
Erel Segal-Halevi

นี่เป็นโซลูชันที่สะอาดสำหรับฉันยกเว้นความจริงที่ว่า @ErelSegalHalevi ชี้ให้เห็นว่าการตรวจสอบว่าค่าที่มีอยู่ในแผนที่จะเป็นไปไม่ได้ตามที่คุณระบุตัวเปรียบเทียบหรือไม่ map.put ("1", "หนึ่ง"); map.put ("2", "สอง"); map.put ("3", "สาม"); map.put ("4", "สี่"); map.put ("5", "Five"); map.containKey ("1") จะคืนค่าเท็จเสมอถ้าคุณคืนค่าวัตถุใหม่ในฟังก์ชั่น sortByValues ​​() เช่น return TreeMap ใหม่ <K, V> (sortByValues); แก้ปัญหา ขอบคุณ Abhi
abhi

ค่อนข้างเหมือนกันกับคำตอบของ user157196 และ Carter Page Carter Page มีการแก้ไข LinkedHashMap
Kirby

บรรทัดที่ 4 ของการแก้ปัญหาควรเป็น int compar = map.get (k1) .compareTo (map.get (k2)); ถ้าคุณต้องการเรียงลำดับจากน้อยไปมาก
www.Decompiler.com

19

เพื่อให้บรรลุสิ่งนี้ด้วยคุณสมบัติใหม่ใน Java 8:

import static java.util.Map.Entry.comparingByValue;
import static java.util.stream.Collectors.toList;

<K, V> List<Entry<K, V>> sort(Map<K, V> map, Comparator<? super V> comparator) {
    return map.entrySet().stream().sorted(comparingByValue(comparator)).collect(toList());
}

รายการถูกเรียงลำดับตามค่าโดยใช้ตัวเปรียบเทียบที่กำหนด อีกทางหนึ่งหากค่าของคุณเปรียบเทียบกันไม่ต้องมีการเปรียบเทียบที่ชัดเจน:

<K, V extends Comparable<? super V>> List<Entry<K, V>> sort(Map<K, V> map) {
    return map.entrySet().stream().sorted(comparingByValue()).collect(toList());
}

รายการที่ส่งคืนเป็นสแน็ปช็อตของแผนที่ที่กำหนดในเวลาที่วิธีการนี้เรียกว่าดังนั้นจะไม่สะท้อนการเปลี่ยนแปลงอื่น ๆ ที่ตามมา สำหรับมุมมอง iterable สดของแผนที่:

<K, V extends Comparable<? super V>> Iterable<Entry<K, V>> sort(Map<K, V> map) {
    return () -> map.entrySet().stream().sorted(comparingByValue()).iterator();
}

การทำซ้ำที่ส่งคืนจะสร้างภาพรวมใหม่ของแผนที่ที่กำหนดในแต่ละครั้งที่มีการทำซ้ำดังนั้นหากมีการแก้ไขพร้อมกันก็จะสะท้อนสถานะปัจจุบันของแผนที่เสมอ


สิ่งนี้จะคืนรายการของรายการแทนที่จะเป็นแผนที่ที่เรียงตามค่า รุ่นอื่นที่ส่งคืนแผนที่: stackoverflow.com/a/22132422/829571
assylias

17

สร้างเครื่องมือเปรียบเทียบที่กำหนดเองและใช้มันในขณะที่สร้างวัตถุ TreeMap ใหม่

class MyComparator implements Comparator<Object> {

    Map<String, Integer> map;

    public MyComparator(Map<String, Integer> map) {
        this.map = map;
    }

    public int compare(Object o1, Object o2) {

        if (map.get(o2) == map.get(o1))
            return 1;
        else
            return ((Integer) map.get(o2)).compareTo((Integer)     
                                                            map.get(o1));

    }
}

ใช้รหัสด้านล่างในฟังก์ชั่นหลักของคุณ

    Map<String, Integer> lMap = new HashMap<String, Integer>();
    lMap.put("A", 35);
    lMap.put("B", 75);
    lMap.put("C", 50);
    lMap.put("D", 50);

    MyComparator comparator = new MyComparator(lMap);

    Map<String, Integer> newMap = new TreeMap<String, Integer>(comparator);
    newMap.putAll(lMap);
    System.out.println(newMap);

เอาท์พุท:

{B=75, D=50, C=50, A=35}

ในกรณีที่ค่าเท่ากันฉันเปลี่ยนบรรทัด "return 1" เพื่อเปรียบเทียบคีย์: "return ((String) o1) .compareTo ((String) o2);"
gjgjgj

14

ในขณะที่ฉันยอมรับว่าความต้องการคงที่ในการเรียงลำดับแผนที่อาจเป็นกลิ่นฉันคิดว่ารหัสต่อไปนี้เป็นวิธีที่ง่ายที่สุดในการทำโดยไม่ต้องใช้โครงสร้างข้อมูลที่แตกต่างกัน

public class MapUtilities {

public static <K, V extends Comparable<V>> List<Entry<K, V>> sortByValue(Map<K, V> map) {
    List<Entry<K, V>> entries = new ArrayList<Entry<K, V>>(map.entrySet());
    Collections.sort(entries, new ByValue<K, V>());
    return entries;
}

private static class ByValue<K, V extends Comparable<V>> implements Comparator<Entry<K, V>> {
    public int compare(Entry<K, V> o1, Entry<K, V> o2) {
        return o1.getValue().compareTo(o2.getValue());
    }
}

}

และนี่คือการทดสอบหน่วยที่ไม่สมบูรณ์อย่างน่าละอาย:

public class MapUtilitiesTest extends TestCase {
public void testSorting() {
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("One", 1);
    map.put("Two", 2);
    map.put("Three", 3);

    List<Map.Entry<String, Integer>> sorted = MapUtilities.sortByValue(map);
    assertEquals("First", "One", sorted.get(0).getKey());
    assertEquals("Second", "Two", sorted.get(1).getKey());
    assertEquals("Third", "Three", sorted.get(2).getKey());
}

}

ผลที่ได้คือรายการเรียงลำดับของวัตถุ Map.Entry ซึ่งคุณสามารถรับคีย์และค่า


วิธีนี้ง่ายและง่ายกว่าการสร้างวัตถุแผนที่ <V, รายการ <K>> ที่มีเอฟเฟกต์เหมือนกัน ค่าไม่ควรเป็นกุญแจในวัตถุแผนที่สิ่งที่คุณกำลังมองหาจริงๆคือรายการในสถานการณ์นี้ IMHO
Jeff Wu

วิธีการแก้ปัญหานี้ไม่ได้ทำงานที่มีจำนวนมากของค่าก็เมามีจำนวนของฉัน (มูลค่าที่เกี่ยวข้องกับแต่ละคีย์)
แซมเลวิน

1
มันแปลกมาก คุณสามารถทำอย่างละเอียด? ผลลัพธ์ของคุณคืออะไรและอะไรคือผลลัพธ์ที่คุณคาดหวัง
Lyudmil

12

ใช้เครื่องมือเปรียบเทียบทั่วไปเช่น:

final class MapValueComparator<K,V extends Comparable<V>> implements Comparator<K> {

    private Map<K,V> map;

    private MapValueComparator() {
        super();
    }

    public MapValueComparator(Map<K,V> map) {
        this();
        this.map = map;
    }

    public int compare(K o1, K o2) {
        return map.get(o1).compareTo(map.get(o2));
    }
}

11

คำตอบที่โหวตให้มากที่สุดจะไม่ทำงานเมื่อคุณมี 2 รายการที่เท่ากับ TreeMap ทำให้ค่าที่เท่ากันหมด

exmaple: แผนที่ที่ไม่เรียงลำดับ

คีย์ / ค่า: D / 67.3
คีย์ / ค่า: A / 99.5
คีย์ / ค่า: B / 67.4
คีย์ / ค่า: C / 67.5
คีย์ / ค่า: E / 99.5

ผล

คีย์ / ค่า: A / 99.5
คีย์ / ค่า: C / 67.5
คีย์ / ค่า: B / 67.4
คีย์ / ค่า: D / 67.3

ดังนั้นออกไป E !!

สำหรับฉันมันใช้งานได้ดีในการปรับตัวเปรียบเทียบถ้ามันไม่เท่ากับ 0 แต่ -1

ในตัวอย่าง:

คลาส ValueComparator ใช้เครื่องมือเปรียบเทียบ

ฐานแผนที่ ValueComparator สาธารณะ (ฐานแผนที่) {this.base = ฐาน; }

การเปรียบเทียบสาธารณะ (Object a, Object b) {

if((Double)base.get(a) < (Double)base.get(b)) {
  return 1;
} else if((Double)base.get(a) == (Double)base.get(b)) {
  return -1;
} else {
  return -1;
}

}}

ตอนนี้มันกลับมา:

แผนที่ที่ไม่เรียงลำดับ:

คีย์ / ค่า: D / 67.3
คีย์ / ค่า: A / 99.5
คีย์ / ค่า: B / 67.4
คีย์ / ค่า: C / 67.5
คีย์ / ค่า: E / 99.5

ผล:

คีย์ / ค่า: A / 99.5
คีย์ / ค่า: E / 99.5
คีย์ / ค่า: C / 67.5
คีย์ / ค่า: B / 67.4
คีย์ / ค่า: D / 67.3

เป็นการตอบสนองต่อเอเลี่ยน (2011 พ.ย. 22): ฉันใช้โซลูชันนี้สำหรับแผนที่ของรหัสจำนวนเต็มและชื่อ แต่ความคิดเหมือนกันดังนั้นอาจเป็นรหัสข้างต้นไม่ถูกต้อง (ฉันจะเขียนในการทดสอบ และให้รหัสที่ถูกต้องแก่คุณ) นี่คือรหัสสำหรับการจัดเรียงแผนที่ตามโซลูชันด้านบน:

package nl.iamit.util;

import java.util.Comparator;
import java.util.Map;

public class Comparators {


    public static class MapIntegerStringComparator implements Comparator {

        Map<Integer, String> base;

        public MapIntegerStringComparator(Map<Integer, String> base) {
            this.base = base;
        }

        public int compare(Object a, Object b) {

            int compare = ((String) base.get(a))
                    .compareTo((String) base.get(b));
            if (compare == 0) {
                return -1;
            }
            return compare;
        }
    }


}

และนี่คือคลาสทดสอบ (ฉันเพิ่งทดสอบและใช้งานได้กับ Integer, String Map:

package test.nl.iamit.util;

import java.util.HashMap;
import java.util.TreeMap;
import nl.iamit.util.Comparators;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;

public class TestComparators {


    @Test
    public void testMapIntegerStringComparator(){
        HashMap<Integer, String> unSoretedMap = new HashMap<Integer, String>();
        Comparators.MapIntegerStringComparator bvc = new Comparators.MapIntegerStringComparator(
                unSoretedMap);
        TreeMap<Integer, String> sorted_map = new TreeMap<Integer, String>(bvc);
        //the testdata:
        unSoretedMap.put(new Integer(1), "E");
        unSoretedMap.put(new Integer(2), "A");
        unSoretedMap.put(new Integer(3), "E");
        unSoretedMap.put(new Integer(4), "B");
        unSoretedMap.put(new Integer(5), "F");

        sorted_map.putAll(unSoretedMap);

        Object[] targetKeys={new Integer(2),new Integer(4),new Integer(3),new Integer(1),new Integer(5) };
        Object[] currecntKeys=sorted_map.keySet().toArray();

        assertArrayEquals(targetKeys,currecntKeys);
    }
}

นี่คือรหัสสำหรับตัวเปรียบเทียบของแผนที่:

public static class MapStringDoubleComparator implements Comparator {

    Map<String, Double> base;

    public MapStringDoubleComparator(Map<String, Double> base) {
        this.base = base;
    }

    //note if you want decending in stead of ascending, turn around 1 and -1
    public int compare(Object a, Object b) {
        if ((Double) base.get(a) == (Double) base.get(b)) {
            return 0;
        } else if((Double) base.get(a) < (Double) base.get(b)) {
            return -1;
        }else{
            return 1;
        }
    }
}

และนี่คือบทพิสูจน์สำหรับสิ่งนี้:

@Test
public void testMapStringDoubleComparator(){
    HashMap<String, Double> unSoretedMap = new HashMap<String, Double>();
    Comparators.MapStringDoubleComparator bvc = new Comparators.MapStringDoubleComparator(
            unSoretedMap);
    TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc);
    //the testdata:
    unSoretedMap.put("D",new Double(67.3));
    unSoretedMap.put("A",new Double(99.5));
    unSoretedMap.put("B",new Double(67.4));
    unSoretedMap.put("C",new Double(67.5));
    unSoretedMap.put("E",new Double(99.5));

    sorted_map.putAll(unSoretedMap);

    Object[] targetKeys={"D","B","C","E","A"};
    Object[] currecntKeys=sorted_map.keySet().toArray();

    assertArrayEquals(targetKeys,currecntKeys);
}

จาก cource คุณสามารถทำให้มันเป็นแบบทั่วไปได้มากขึ้น แต่ฉันต้องการเพียงแค่ 1 เคส (แผนที่)


คุณถูกต้องมีข้อผิดพลาดบางอย่างในรหัสที่ฉันให้ตอนแรก! ฉันหวังว่าการแก้ไขล่าสุดของฉันจะช่วยคุณได้
michel.iamit

9

แทนการใช้เป็นบางส่วนฉันขอแนะนำให้ใช้Collections.sort Arrays.sortจริงๆแล้วอะไรCollections.sortคือสิ่งนี้:

public static <T extends Comparable<? super T>> void sort(List<T> list) {
    Object[] a = list.toArray();
    Arrays.sort(a);
    ListIterator<T> i = list.listIterator();
    for (int j=0; j<a.length; j++) {
        i.next();
        i.set((T)a[j]);
    }
}

มันก็เรียกร้องในรายการและการใช้งานแล้วtoArray Arrays.sortวิธีนี้รายการแผนที่ทั้งหมดจะถูกคัดลอกสามครั้ง: หนึ่งครั้งจากแผนที่ไปยังรายการชั่วคราว (ไม่ว่าจะเป็น LinkedList หรือ ArrayList) จากนั้นไปยังอาร์เรย์ชั่วคราวและสุดท้ายไปยังแผนที่ใหม่

วิธีการแก้ปัญหาของฉันสรุปขั้นตอนนี้เพียงครั้งเดียวเนื่องจากไม่ได้สร้าง LinkedList ที่ไม่จำเป็น นี่คือรหัสเป็นมิตรกับทั่วไปและมีประสิทธิภาพสูงสุด:

public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) 
{
    @SuppressWarnings("unchecked")
    Map.Entry<K,V>[] array = map.entrySet().toArray(new Map.Entry[map.size()]);

    Arrays.sort(array, new Comparator<Map.Entry<K, V>>() 
    {
        public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) 
        {
            return e1.getValue().compareTo(e2.getValue());
        }
    });

    Map<K, V> result = new LinkedHashMap<K, V>();
    for (Map.Entry<K, V> entry : array)
        result.put(entry.getKey(), entry.getValue());

    return result;
}

8

นี่คือรูปแบบต่าง ๆ ของคำตอบของ Anthony ซึ่งใช้ไม่ได้หากมีค่าซ้ำกัน:

public static <K, V extends Comparable<V>> Map<K, V> sortMapByValues(final Map<K, V> map) {
    Comparator<K> valueComparator =  new Comparator<K>() {
        public int compare(K k1, K k2) {
            final V v1 = map.get(k1);
            final V v2 = map.get(k2);

            /* Not sure how to handle nulls ... */
            if (v1 == null) {
                return (v2 == null) ? 0 : 1;
            }

            int compare = v2.compareTo(v1);
            if (compare != 0)
            {
                return compare;
            }
            else
            {
                Integer h1 = k1.hashCode();
                Integer h2 = k2.hashCode();
                return h2.compareTo(h1);
            }
        }
    };
    Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
    sortedByValues.putAll(map);
    return sortedByValues;
}

โปรดทราบว่ามันค่อนข้างจะอยู่ในอากาศถึงวิธีจัดการกับโมฆะ

ข้อได้เปรียบที่สำคัญอย่างหนึ่งของวิธีนี้คือการส่งคืนแผนที่ซึ่งแตกต่างจากโซลูชันอื่น ๆ ที่นำเสนอที่นี่


ไม่ถูกต้องวิธีการของฉันจะทำงานหากมีค่าซ้ำกัน ฉันใช้กับแผนที่ที่มีปุ่มมากกว่า 100 ปุ่มและมีค่าเป็น "1"
แอนโทนี่

8

วิธีการที่ดีที่สุด

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry; 

public class OrderByValue {

  public static void main(String a[]){
    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("java", 20);
    map.put("C++", 45);
    map.put("Unix", 67);
    map.put("MAC", 26);
    map.put("Why this kolavari", 93);
    Set<Entry<String, Integer>> set = map.entrySet();
    List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(set);
    Collections.sort( list, new Comparator<Map.Entry<String, Integer>>()
    {
        public int compare( Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2 )
        {
            return (o1.getValue()).compareTo( o2.getValue() );//Ascending order
            //return (o2.getValue()).compareTo( o1.getValue() );//Descending order
        }
    } );
    for(Map.Entry<String, Integer> entry:list){
        System.out.println(entry.getKey()+" ==== "+entry.getValue());
    }
  }}

เอาท์พุต

java ==== 20

MAC ==== 26

C++ ==== 45

Unix ==== 67

Why this kolavari ==== 93

7

ปัญหาหลัก. หากคุณใช้คำตอบแรก (Google นำคุณมาที่นี่) ให้เปลี่ยนเครื่องมือเปรียบเทียบเพื่อเพิ่มส่วนคำสั่งที่เท่ากันมิฉะนั้นคุณจะไม่สามารถรับค่าจาก sort_map โดยใช้คีย์:

public int compare(String a, String b) {
        if (base.get(a) > base.get(b)) {
            return 1;
        } else if (base.get(a) < base.get(b)){
            return -1;
        } 

        return 0;
        // returning 0 would merge keys
    }

ตอนนี้เมื่อคุณเพิ่มสองรายการที่มีค่าเท่ากันพวกเขาจะถูกรวมเข้าด้วยกันคุณควรคืนค่า 0 เท่านั้นหากคุณแน่ใจว่าวัตถุเหมือนกัน (เท่ากับ)
Masood_mj

7

มีคำตอบมากมายสำหรับคำถามนี้อยู่แล้ว แต่ไม่มีใครให้สิ่งที่ฉันกำลังมองหาการใช้งานแผนที่ที่ส่งคืนคีย์และรายการที่เรียงลำดับตามค่าที่เกี่ยวข้องและรักษาคุณสมบัตินี้ไว้เนื่องจากมีการแก้ไขคีย์และค่าในแผนที่ สองอื่น ๆ คำถามถามสำหรับการนี้โดยเฉพาะ

ฉันปรุงตัวอย่างที่เป็นมิตรทั่วไปที่ช่วยแก้กรณีการใช้งานนี้ การใช้งานนี้ไม่เป็นไปตามสัญญาทั้งหมดของอินเทอร์เฟซแผนที่เช่นการสะท้อนค่าการเปลี่ยนแปลงและการลบในชุดการส่งคืนจาก keySet () และ entrySet () ในวัตถุต้นฉบับ ฉันรู้สึกว่าวิธีแก้ปัญหานั้นใหญ่เกินไปที่จะรวมไว้ในคำตอบของ Stack Overflow หากฉันจัดการเพื่อสร้างการใช้งานที่สมบูรณ์ยิ่งขึ้นบางทีฉันอาจโพสต์ไว้ที่ Github และเชื่อมโยงไปยังคำตอบนี้ในเวอร์ชันที่อัปเดตแล้ว

import java.util.*;

/**
 * A map where {@link #keySet()} and {@link #entrySet()} return sets ordered
 * by associated values based on the the comparator provided at construction
 * time. The order of two or more keys with identical values is not defined.
 * <p>
 * Several contracts of the Map interface are not satisfied by this minimal
 * implementation.
 */
public class ValueSortedMap<K, V> extends HashMap<K, V> {
    protected Map<V, Collection<K>> valueToKeysMap;

    // uses natural order of value object, if any
    public ValueSortedMap() {
        this((Comparator<? super V>) null);
    }

    public ValueSortedMap(Comparator<? super V> valueComparator) {
        this.valueToKeysMap = new TreeMap<V, Collection<K>>(valueComparator);
    }

    public boolean containsValue(Object o) {
        return valueToKeysMap.containsKey(o);
    }

    public V put(K k, V v) {
        V oldV = null;
        if (containsKey(k)) {
            oldV = get(k);
            valueToKeysMap.get(oldV).remove(k);
        }
        super.put(k, v);
        if (!valueToKeysMap.containsKey(v)) {
            Collection<K> keys = new ArrayList<K>();
            keys.add(k);
            valueToKeysMap.put(v, keys);
        } else {
            valueToKeysMap.get(v).add(k);
        }
        return oldV;
    }

    public void putAll(Map<? extends K, ? extends V> m) {
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }

    public V remove(Object k) {
        V oldV = null;
        if (containsKey(k)) {
            oldV = get(k);
            super.remove(k);
            valueToKeysMap.get(oldV).remove(k);
        }
        return oldV;
    }

    public void clear() {
        super.clear();
        valueToKeysMap.clear();
    }

    public Set<K> keySet() {
        LinkedHashSet<K> ret = new LinkedHashSet<K>(size());
        for (V v : valueToKeysMap.keySet()) {
            Collection<K> keys = valueToKeysMap.get(v);
            ret.addAll(keys);
        }
        return ret;
    }

    public Set<Map.Entry<K, V>> entrySet() {
        LinkedHashSet<Map.Entry<K, V>> ret = new LinkedHashSet<Map.Entry<K, V>>(size());
        for (Collection<K> keys : valueToKeysMap.values()) {
            for (final K k : keys) {
                final V v = get(k);
                ret.add(new Map.Entry<K,V>() {
                    public K getKey() {
                        return k;
                    }

                    public V getValue() {
                        return v;
                    }

                    public V setValue(V v) {
                        throw new UnsupportedOperationException();
                    }
                });
            }
        }
        return ret;
    }
}

หากไม่สามารถเปรียบเทียบและเปรียบเทียบได้จะทำอย่างไร?
Ved Prakash

ไม่แน่ใจว่าฉันเข้าใจกรณีการใช้ของคุณหรือเปล่าบางทีคุณสามารถทำอย่างละเอียดได้ หากวัตถุที่คุณต้องการใช้เป็นค่าไม่สามารถเปรียบเทียบได้คุณจะต้องแปลงเป็นวัตถุที่เป็น
David Bleckmann

6

สายเข้า

ด้วยการถือกำเนิดของ Java-8 เราสามารถใช้สตรีมสำหรับการจัดการข้อมูลในวิธีที่ง่าย / รวบรัด คุณสามารถใช้สตรีมเพื่อเรียงลำดับรายการแผนที่ตามค่าและสร้างLinkedHashMapซึ่งรักษาการย้ำลำดับการแทรก

เช่น:

LinkedHashMap sortedByValueMap = map.entrySet().stream()
                .sorted(comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey))     //first sorting by Value, then sorting by Key(entries with same value)
                .collect(LinkedHashMap::new,(map,entry) -> map.put(entry.getKey(),entry.getValue()),LinkedHashMap::putAll);

สำหรับการสั่งซื้อย้อนกลับแทนที่:

comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)

กับ

comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey).reversed()

ขอบคุณสำหรับรุ่นที่แสดงความคิดเห็นนี้ คำถามหนึ่ง: อะไรคือความแตกต่างของการใช้Entry.comparingByValue()(เป็นคำตอบ assylias เหนือstackoverflow.com/a/22132422/1480587 ) หรือcomparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)ที่คุณใช้? ฉันเข้าใจว่าคุณเปรียบเทียบคีย์ด้วยหากค่าเหมือนกันใช่ไหม ฉันสังเกตเห็นว่าการเรียงลำดับทำให้องค์ประกอบมีค่าเท่ากันดังนั้นการเรียงลำดับโดยใช้คีย์จำเป็นหรือไม่หากมีการเรียงลำดับคีย์ก่อนหน้านี้
ปีเตอร์ต.

6

แผนที่ที่กำหนด

   Map<String, Integer> wordCounts = new HashMap<>();
    wordCounts.put("USA", 100);
    wordCounts.put("jobs", 200);
    wordCounts.put("software", 50);
    wordCounts.put("technology", 70);
    wordCounts.put("opportunity", 200);

จัดเรียงแผนที่ตามค่าในลำดับจากน้อยไปหามาก

Map<String,Integer>  sortedMap =  wordCounts.entrySet().
                                                stream().
                                                sorted(Map.Entry.comparingByValue()).
        collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
    System.out.println(sortedMap);

จัดเรียงแผนที่ตามค่าในลำดับที่ได้รับ

Map<String,Integer>  sortedMapReverseOrder =  wordCounts.entrySet().
            stream().
            sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).
            collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
    System.out.println(sortedMapReverseOrder);

เอาท์พุท:

{ซอฟต์แวร์ = 50, เทคโนโลยี = 70, สหรัฐอเมริกา = 100, งาน = 200, โอกาส = 200}

{jobs = 200, โอกาส = 200, สหรัฐอเมริกา = 100, เทคโนโลยี = 70, ซอฟต์แวร์ = 50}


ฉันเดาว่าแผนที่ลดจริง ๆ "ลด" มันเป็นทางออกที่ดี
ha9u63ar

Thanks @ ha9u63ar
Arpan Saini

มันใช้งานได้ แต่ฉันไม่เข้าใจว่าลำดับขององค์ประกอบเข้ามาเล่นใน HashMap อย่างไร
Ali Tou

5

ขึ้นอยู่กับบริบทการใช้java.util.LinkedHashMap<T>ซึ่งจะจดจำลำดับของรายการที่จะถูกวางลงในแผนที่ Collections.sort()มิฉะนั้นถ้าคุณจำเป็นต้องเรียงลำดับค่าขึ้นอยู่กับการสั่งซื้อธรรมชาติของพวกเขาผมจะแนะนำให้รักษารายการแยกต่างหากซึ่งสามารถเรียงผ่าน


ฉันไม่เห็นว่าทำไมนี่ถึง -1 แต่ฉะนี้ LinkedHashMap อาจเป็นทางออกที่ดีที่สุดสำหรับฉันฉันแค่พยายามคิดว่ามันแพงแค่ไหนที่จะทิ้งและสร้าง LinkedHashMap ใหม่
NobleUplift

5

เนื่องจากTreeMap <> ใช้ไม่ได้กับค่าที่เท่ากันฉันจึงใช้สิ่งนี้:

private <K, V extends Comparable<? super V>> List<Entry<K, V>> sort(Map<K, V> map)     {
    List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>(map.entrySet());
    Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
        public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
            return o1.getValue().compareTo(o2.getValue());
        }
    });

    return list;
}

คุณอาจต้องการที่จะใส่รายการในLinkedHashMapแต่ถ้าคุณจะทำซ้ำมันทันทีนั่นคือฟุ่มเฟือย ...


ถูกต้อง แต่เครื่องมือเปรียบเทียบของคุณไม่สามารถจัดการกับค่าได้เท่ากับกรณี
Sebastien Lorber

5

นี่มันซับซ้อนเกินไป แผนที่ไม่ควรทำงานเช่นเรียงลำดับตามค่า วิธีที่ง่ายที่สุดคือการสร้างคลาสของคุณเองเพื่อให้เหมาะกับความต้องการของคุณ

ในตัวอย่างที่ต่ำกว่าคุณควรจะเพิ่ม TreeMap เครื่องมือเปรียบเทียบในตำแหน่งที่ * เป็น แต่ด้วย java API มันให้คีย์เปรียบเทียบเท่านั้นไม่ใช่ค่า ตัวอย่างทั้งหมดที่ระบุไว้ที่นี่ขึ้นอยู่กับ 2 Maps หนึ่งแฮชและหนึ่งต้นใหม่ ซึ่งเป็นเลขคี่

ตัวอย่าง:

Map<Driver driver, Float time> map = new TreeMap<Driver driver, Float time>(*);

ดังนั้นเปลี่ยนแผนที่เป็นชุดด้วยวิธีนี้:

ResultComparator rc = new ResultComparator();
Set<Results> set = new TreeSet<Results>(rc);

คุณจะสร้างชั้นResults,

public class Results {
    private Driver driver;
    private Float time;

    public Results(Driver driver, Float time) {
        this.driver = driver;
        this.time = time;
    }

    public Float getTime() {
        return time;
    }

    public void setTime(Float time) {
        this.time = time;
    }

    public Driver getDriver() {
        return driver;
    }

    public void setDriver (Driver driver) {
        this.driver = driver;
    }
}

และคลาส Comparator:

public class ResultsComparator implements Comparator<Results> {
    public int compare(Results t, Results t1) {
        if (t.getTime() < t1.getTime()) {
            return 1;
        } else if (t.getTime() == t1.getTime()) {
            return 0;
        } else {
            return -1;
        }
    }
}

วิธีนี้คุณสามารถเพิ่มการพึ่งพาได้ง่ายขึ้น

และเป็นจุดสุดท้ายฉันจะเพิ่มตัววนซ้ำอย่างง่าย:

Iterator it = set.iterator();
while (it.hasNext()) {
    Results r = (Results)it.next();
    System.out.println( r.getDriver().toString
        //or whatever that is related to Driver class -getName() getSurname()
        + " "
        + r.getTime()
        );
}

4

ขึ้นอยู่กับรหัส @devinmoore วิธีการเรียงลำดับแผนที่โดยใช้ข้อมูลทั่วไปและสนับสนุนการสั่งซื้อทั้งขึ้นและลง

/**
 * Sort a map by it's keys in ascending order. 
 *  
 * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
 * @author Maxim Veksler
 */
public static <K, V> LinkedHashMap<K, V> sortMapByKey(final Map<K, V> map) {
    return sortMapByKey(map, SortingOrder.ASCENDING);
}

/**
 * Sort a map by it's values in ascending order.
 *  
 * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
 * @author Maxim Veksler
 */
public static <K, V> LinkedHashMap<K, V> sortMapByValue(final Map<K, V> map) {
    return sortMapByValue(map, SortingOrder.ASCENDING);
}

/**
 * Sort a map by it's keys.
 *  
 * @param sortingOrder {@link SortingOrder} enum specifying requested sorting order. 
 * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
 * @author Maxim Veksler
 */
public static <K, V> LinkedHashMap<K, V> sortMapByKey(final Map<K, V> map, final SortingOrder sortingOrder) {
    Comparator<Map.Entry<K, V>> comparator = new Comparator<Entry<K,V>>() {
        public int compare(Entry<K, V> o1, Entry<K, V> o2) {
            return comparableCompare(o1.getKey(), o2.getKey(), sortingOrder);
        }
    };

    return sortMap(map, comparator);
}

/**
 * Sort a map by it's values.
 *  
 * @param sortingOrder {@link SortingOrder} enum specifying requested sorting order. 
 * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
 * @author Maxim Veksler
 */
public static <K, V> LinkedHashMap<K, V> sortMapByValue(final Map<K, V> map, final SortingOrder sortingOrder) {
    Comparator<Map.Entry<K, V>> comparator = new Comparator<Entry<K,V>>() {
        public int compare(Entry<K, V> o1, Entry<K, V> o2) {
            return comparableCompare(o1.getValue(), o2.getValue(), sortingOrder);
        }
    };

    return sortMap(map, comparator);
}

@SuppressWarnings("unchecked")
private static <T> int comparableCompare(T o1, T o2, SortingOrder sortingOrder) {
    int compare = ((Comparable<T>)o1).compareTo(o2);

    switch (sortingOrder) {
    case ASCENDING:
        return compare;
    case DESCENDING:
        return (-1) * compare;
    }

    return 0;
}

/**
 * Sort a map by supplied comparator logic.
 *  
 * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
 * @author Maxim Veksler
 */
public static <K, V> LinkedHashMap<K, V> sortMap(final Map<K, V> map, final Comparator<Map.Entry<K, V>> comparator) {
    // Convert the map into a list of key,value pairs.
    List<Map.Entry<K, V>> mapEntries = new LinkedList<Map.Entry<K, V>>(map.entrySet());

    // Sort the converted list according to supplied comparator.
    Collections.sort(mapEntries, comparator);

    // Build a new ordered map, containing the same entries as the old map.  
    LinkedHashMap<K, V> result = new LinkedHashMap<K, V>(map.size() + (map.size() / 20));
    for(Map.Entry<K, V> entry : mapEntries) {
        // We iterate on the mapEntries list which is sorted by the comparator putting new entries into 
        // the targeted result which is a sorted map. 
        result.put(entry.getKey(), entry.getValue());
    }

    return result;
}

/**
 * Sorting order enum, specifying request result sort behavior.
 * @author Maxim Veksler
 *
 */
public static enum SortingOrder {
    /**
     * Resulting sort will be from smaller to biggest.
     */
    ASCENDING,
    /**
     * Resulting sort will be from biggest to smallest.
     */
    DESCENDING
}

จากนั้นอีกครั้งอาจเป็นทางออกที่ดีกว่าคือเพียงใช้แผนที่การเรียงลำดับตัวเองในกรณีที่ใช้ org.apache.commons.collections.bidimap.TreeBidiMap
# # # Maximak Veksler

4

นี่คือวิธีการแก้ปัญหา OO (เช่นไม่ได้ใช้staticวิธีการ):

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class SortableValueMap<K, V extends Comparable<V>>
  extends LinkedHashMap<K, V> {
  public SortableValueMap() { }

  public SortableValueMap( Map<K, V> map ) {
    super( map );
  }

  public void sortByValue() {
    List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>( entrySet() );

    Collections.sort( list, new Comparator<Map.Entry<K, V>>() {
      public int compare( Map.Entry<K, V> entry1, Map.Entry<K, V> entry2 ) {
        return entry1.getValue().compareTo( entry2.getValue() );
      }
    });

    clear();

    for( Map.Entry<K, V> entry : list ) {
      put( entry.getKey(), entry.getValue() );
    }
  }

  private static void print( String text, Map<String, Double> map ) {
    System.out.println( text );

    for( String key : map.keySet() ) {
      System.out.println( "key/value: " + key + "/" + map.get( key ) );
    }
  }

  public static void main( String[] args ) {
    SortableValueMap<String, Double> map =
      new SortableValueMap<String, Double>();

    map.put( "A", 67.5 );
    map.put( "B", 99.5 );
    map.put( "C", 82.4 );
    map.put( "D", 42.0 );

    print( "Unsorted map", map );
    map.sortByValue();
    print( "Sorted map", map );
  }
}

ขอบริจาคให้กับสาธารณสมบัติ


4

Afaik วิธีที่สะอาดที่สุดคือการใช้คอลเล็กชันเพื่อจัดเรียงแผนที่ตามค่า:

Map<String, Long> map = new HashMap<String, Long>();
// populate with data to sort on Value
// use datastructure designed for sorting

Queue queue = new PriorityQueue( map.size(), new MapComparable() );
queue.addAll( map.entrySet() );

// get a sorted map
LinkedHashMap<String, Long> linkedMap = new LinkedHashMap<String, Long>();

for (Map.Entry<String, Long> entry; (entry = queue.poll())!=null;) {
    linkedMap.put(entry.getKey(), entry.getValue());
}

public static class MapComparable implements Comparator<Map.Entry<String, Long>>{

  public int compare(Entry<String, Long> e1, Entry<String, Long> e2) {
    return e1.getValue().compareTo(e2.getValue());
  }
}

4

การเปลี่ยนแปลงง่าย ๆ บางอย่างเพื่อให้แผนที่ที่เรียงลำดับด้วยคู่ที่มีค่าซ้ำกัน ในเมธอดการเปรียบเทียบ (คลาส ValueComparator) เมื่อค่าเท่ากันอย่าส่งคืน 0 แต่ส่งคืนผลลัพธ์ของการเปรียบเทียบคีย์ 2 คีย์ต่างกันในแผนที่เพื่อให้คุณเก็บค่าที่ซ้ำกันได้สำเร็จ (ซึ่งเรียงลำดับตามคีย์) ดังนั้นตัวอย่างข้างต้นสามารถแก้ไขได้ดังนี้:

    public int compare(Object a, Object b) {

        if((Double)base.get(a) < (Double)base.get(b)) {
          return 1;
        } else if((Double)base.get(a) == (Double)base.get(b)) {
          return ((String)a).compareTo((String)b);
        } else {
          return -1;
        }
      }
    }

4

แน่นอนว่าทางออกของสตีเฟ่นนั้นยอดเยี่ยมจริงๆ แต่สำหรับผู้ที่ไม่สามารถใช้ Guava ได้:

นี่คือทางออกของฉันสำหรับการเรียงลำดับตามมูลค่าแผนที่ วิธีการแก้ปัญหานี้จัดการกรณีที่มีค่าเดียวกันสองครั้ง ฯลฯ ...

// If you want to sort a map by value, and if there can be twice the same value:

// here is your original map
Map<String,Integer> mapToSortByValue = new HashMap<String, Integer>();
mapToSortByValue.put("A", 3);
mapToSortByValue.put("B", 1);
mapToSortByValue.put("C", 3);
mapToSortByValue.put("D", 5);
mapToSortByValue.put("E", -1);
mapToSortByValue.put("F", 1000);
mapToSortByValue.put("G", 79);
mapToSortByValue.put("H", 15);

// Sort all the map entries by value
Set<Map.Entry<String,Integer>> set = new TreeSet<Map.Entry<String,Integer>>(
        new Comparator<Map.Entry<String,Integer>>(){
            @Override
            public int compare(Map.Entry<String,Integer> obj1, Map.Entry<String,Integer> obj2) {
                Integer val1 = obj1.getValue();
                Integer val2 = obj2.getValue();
                // DUPLICATE VALUE CASE
                // If the values are equals, we can't return 0 because the 2 entries would be considered
                // as equals and one of them would be deleted (because we use a set, no duplicate, remember!)
                int compareValues = val1.compareTo(val2);
                if ( compareValues == 0 ) {
                    String key1 = obj1.getKey();
                    String key2 = obj2.getKey();
                    int compareKeys = key1.compareTo(key2);
                    if ( compareKeys == 0 ) {
                        // what you return here will tell us if you keep REAL KEY-VALUE duplicates in your set
                        // if you want to, do whatever you want but do not return 0 (but don't break the comparator contract!)
                        return 0;
                    }
                    return compareKeys;
                }
                return compareValues;
            }
        }
);
set.addAll(mapToSortByValue.entrySet());


// OK NOW OUR SET IS SORTED COOL!!!!

// And there's nothing more to do: the entries are sorted by value!
for ( Map.Entry<String,Integer> entry : set ) {
    System.out.println("Set entries: " + entry.getKey() + " -> " + entry.getValue());
}




// But if you add them to an hashmap
Map<String,Integer> myMap = new HashMap<String,Integer>();
// When iterating over the set the order is still good in the println...
for ( Map.Entry<String,Integer> entry : set ) {
    System.out.println("Added to result map entries: " + entry.getKey() + " " + entry.getValue());
    myMap.put(entry.getKey(), entry.getValue());
}

// But once they are in the hashmap, the order is not kept!
for ( Integer value : myMap.values() ) {
    System.out.println("Result map values: " + value);
}
// Also this way doesn't work:
// Logic because the entryset is a hashset for hashmaps and not a treeset
// (and even if it was a treeset, it would be on the keys only)
for ( Map.Entry<String,Integer> entry : myMap.entrySet() ) {
    System.out.println("Result map entries: " + entry.getKey() + " -> " + entry.getValue());
}


// CONCLUSION:
// If you want to iterate on a map ordered by value, you need to remember:
// 1) Maps are only sorted by keys, so you can't sort them directly by value
// 2) So you simply CAN'T return a map to a sortMapByValue function
// 3) You can't reverse the keys and the values because you have duplicate values
//    This also means you can't neither use Guava/Commons bidirectionnal treemaps or stuff like that

// SOLUTIONS
// So you can:
// 1) only sort the values which is easy, but you loose the key/value link (since you have duplicate values)
// 2) sort the map entries, but don't forget to handle the duplicate value case (like i did)
// 3) if you really need to return a map, use a LinkedHashMap which keep the insertion order

ผู้บริหาร: http://www.ideone.com/dq3Lu

ผลลัพธ์:

Set entries: E -> -1
Set entries: B -> 1
Set entries: A -> 3
Set entries: C -> 3
Set entries: D -> 5
Set entries: H -> 15
Set entries: G -> 79
Set entries: F -> 1000
Added to result map entries: E -1
Added to result map entries: B 1
Added to result map entries: A 3
Added to result map entries: C 3
Added to result map entries: D 5
Added to result map entries: H 15
Added to result map entries: G 79
Added to result map entries: F 1000
Result map values: 5
Result map values: -1
Result map values: 1000
Result map values: 79
Result map values: 3
Result map values: 1
Result map values: 3
Result map values: 15
Result map entries: D -> 5
Result map entries: E -> -1
Result map entries: F -> 1000
Result map entries: G -> 79
Result map entries: A -> 3
Result map entries: B -> 1
Result map entries: C -> 3
Result map entries: H -> 15

หวังว่ามันจะช่วยให้คนบางคน


3

หากคุณมีคีย์ที่ซ้ำกันและมีเพียงชุดข้อมูลขนาดเล็ก (<1,000) และรหัสของคุณไม่สำคัญกับประสิทธิภาพคุณสามารถทำสิ่งต่อไปนี้ได้:

Map<String,Integer> tempMap=new HashMap<String,Integer>(inputUnsortedMap);
LinkedHashMap<String,Integer> sortedOutputMap=new LinkedHashMap<String,Integer>();

for(int i=0;i<inputUnsortedMap.size();i++){
    Map.Entry<String,Integer> maxEntry=null;
    Integer maxValue=-1;
    for(Map.Entry<String,Integer> entry:tempMap.entrySet()){
        if(entry.getValue()>maxValue){
            maxValue=entry.getValue();
            maxEntry=entry;
        }
    }
    tempMap.remove(maxEntry.getKey());
    sortedOutputMap.put(maxEntry.getKey(),maxEntry.getValue());
}

inputUnsortedMapคืออินพุตไปยังโค้ด

ตัวแปร sortOutputMapจะมีข้อมูลในลำดับการตัดสินใจเมื่อวนซ้ำ หากต้องการเปลี่ยนลำดับเพียงเปลี่ยน> เป็น <ในคำสั่ง if

ไม่ใช่การเรียงลำดับที่เร็วที่สุด แต่ทำงานได้โดยไม่มีการอ้างอิงเพิ่มเติมใด ๆ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.