ट्रीपैप मूल्य के आधार पर छाँटे जाते हैं


137

मैं एक तुलनित्र लिखना चाहता हूं जो मुझे डिफ़ॉल्ट प्राकृतिक आदेश के बजाय ट्रीपैप को मूल्य के आधार पर क्रमबद्ध करने देगा।

मैंने ऐसा कुछ करने की कोशिश की, लेकिन पता नहीं चल सका कि क्या गलत हुआ:

import java.util.*;

class treeMap {
    public static void main(String[] args) {
        System.out.println("the main");
        byValue cmp = new byValue();
        Map<String, Integer> map = new TreeMap<String, Integer>(cmp);
        map.put("de",10);
        map.put("ab", 20);
        map.put("a",5);

        for (Map.Entry<String,Integer> pair: map.entrySet()) {
            System.out.println(pair.getKey()+":"+pair.getValue());
        }
    }
}

class byValue implements Comparator<Map.Entry<String,Integer>> {
    public int compare(Map.Entry<String,Integer> e1, Map.Entry<String,Integer> e2) {
        if (e1.getValue() < e2.getValue()){
            return 1;
        } else if (e1.getValue() == e2.getValue()) {
            return 0;
        } else {
            return -1;
        }
    }
}

मुझे लगता है कि मैं जो पूछ रहा हूं वह यह है: क्या मैं Map.Entryतुलनित्र से पास हो सकता हूं ?


शायद यह आपकी मदद करेगा stackoverflow.com/questions/109383/…
Inv3r53


जवाबों:


179

आप TreeMapमानों को स्वयं सॉर्ट नहीं कर सकते , क्योंकि यह SortedMapविनिर्देश को ख़राब करता है:

Mapकि आगे इसकी चाबियों पर कुल आदेश प्रदान करता है ।

हालाँकि, बाहरी संग्रह का उपयोग करके, आप हमेशा Map.entrySet()अपनी इच्छा के अनुसार छांट सकते हैं , या तो कुंजी, मान, या यहाँ तक कि दो का संयोजन ()।

यहाँ एक सामान्य विधि है कि एक रिटर्न है SortedSetकी Map.Entry, यह देखते हुए एक Mapजिनके मान रहे हैं Comparable:

static <K,V extends Comparable<? super V>>
SortedSet<Map.Entry<K,V>> entriesSortedByValues(Map<K,V> map) {
    SortedSet<Map.Entry<K,V>> sortedEntries = new TreeSet<Map.Entry<K,V>>(
        new Comparator<Map.Entry<K,V>>() {
            @Override public int compare(Map.Entry<K,V> e1, Map.Entry<K,V> e2) {
                int res = e1.getValue().compareTo(e2.getValue());
                return res != 0 ? res : 1;
            }
        }
    );
    sortedEntries.addAll(map.entrySet());
    return sortedEntries;
}

अब आप निम्नलिखित कर सकते हैं:

    Map<String,Integer> map = new TreeMap<String,Integer>();
    map.put("A", 3);
    map.put("B", 2);
    map.put("C", 1);   

    System.out.println(map);
    // prints "{A=3, B=2, C=1}"
    System.out.println(entriesSortedByValues(map));
    // prints "[C=1, B=2, A=3]"

ध्यान दें कि यदि आप या तो SortedSetस्वयं या Map.Entryभीतर को संशोधित करने का प्रयास करते हैं, तो फंकी सामान होगा , क्योंकि यह अब मूल नक्शे का "दृश्य" नहीं entrySet()है।

सामान्यतया, मानचित्र की प्रविष्टियों को इसके मूल्यों के आधार पर क्रमबद्ध करने की आवश्यकता अनिवार्य है।


पर ध्यान दें ==के लिएInteger

आपके मूल तुलनित्र Integerका उपयोग करने की तुलना करता है ==। यह लगभग हमेशा गलत है, के बाद से ==साथ Integerऑपरेंड एक संदर्भ समानता, नहीं मूल्य समानता है।

    System.out.println(new Integer(0) == new Integer(0)); // prints "false"!!!

संबंधित सवाल


5
यदि आप map.put ("डी", 2) जोड़ते हैं; परिणाम अभी भी है "[[सी = 1, बी = 2, ए = 3]" और नहीं "[सी = 1, बी = 2, डी = 2, ए = 3]"
इगोर मिल्ला

3
फिक्स: int Res = e2.getValue (); ComparTo (e1.getValue ()); वापसी Res! = 0 रेस: 1;
बेश्केनदेज़

new Integer (0) == new Integer (0) आप किसी ऑब्जेक्ट के संदर्भ की तुलना करते हैं, और आप दो ऑब्जेक्ट बनाते हैं! आउट पुट बिल्कुल सही और अनुमानित है।
यूरी चेर्नशोव

2
"आम तौर पर बोलना, अपने मूल्यों द्वारा मानचित्र की प्रविष्टियों को क्रमबद्ध करने की आवश्यकता अनिवार्य है" मैं यहां एक शुरुआत कर रहा हूं लेकिन मुझे ऐसा लगता है कि ऐसा करना बहुत ही सामान्य बात होनी चाहिए? उदाहरण के लिए यदि मैं 3 सबसे पुराने व्यक्तियों को मानचित्र में "{ana" = 37, "पीटर" = 43, "जॉन" = 4, "मेरी" = 15, "मैट" = 78}
fartagaintuxux

@igormilla एक सरल कारण है, आपका कोड क्यों काम करता है: Autoboxing Integer.valueOf का उपयोग करता है। और इसके लिए -128 से 127 तक के उदाहरण हैं!
जोकर

75

पॉलीजेनबेलिकेंट का उत्तर लगभग सही है। हालांकि यह एक महत्वपूर्ण बग है। यह उन मानचित्र प्रविष्टियों को नहीं संभालेगा जहाँ मान समान हैं।

यह कोड: ...

Map<String, Integer> nonSortedMap = new HashMap<String, Integer>();
nonSortedMap.put("ape", 1);
nonSortedMap.put("pig", 3);
nonSortedMap.put("cow", 1);
nonSortedMap.put("frog", 2);

for (Entry<String, Integer> entry  : entriesSortedByValues(nonSortedMap)) {
    System.out.println(entry.getKey()+":"+entry.getValue());
}

उत्पादन होगा:

ape:1
frog:2
pig:3

ध्यान दें कि हमारी गाय ने कैसे गायब कर दिया क्योंकि यह हमारे "वान" के साथ "1" मूल्य साझा करता है: हे!

कोड का यह संशोधन उस समस्या को हल करता है:

static <K,V extends Comparable<? super V>> SortedSet<Map.Entry<K,V>> entriesSortedByValues(Map<K,V> map) {
        SortedSet<Map.Entry<K,V>> sortedEntries = new TreeSet<Map.Entry<K,V>>(
            new Comparator<Map.Entry<K,V>>() {
                @Override public int compare(Map.Entry<K,V> e1, Map.Entry<K,V> e2) {
                    int res = e1.getValue().compareTo(e2.getValue());
                    return res != 0 ? res : 1; // Special fix to preserve items with equal values
                }
            }
        );
        sortedEntries.addAll(map.entrySet());
        return sortedEntries;
    }

2
-1। AFASIK Setक्रियान्वयन में एक से अधिक बार एक तत्व होता है। आपने बस उस अड़चन का उल्लंघन किया। से SortedSetएपीआई: नोट आदेश एक क्रमबद्ध सेट द्वारा बनाए रखा है कि बराबरी के अनुरूप होना चाहिए ... । समाधान को Listकार्यान्वयन में बदलना होगा ।
डेक्वे

4
@ डैकवे समान मान कुंजी नहीं
बेलम

1
@ बेलम: हम यहां बात कर रहे Setहैं। चूंकि अनुबंध Comparatorका उल्लंघन करता है , आदि काम नहीं करता है ! इस उदाहरण को आइडोन पर देखेंSetSet.removeSet.contains
डैकवे

3
बस एक नोट, यदि आप स्विच int res= e1.getValue().compareTo(e2.getValue());करते हैं int res= e2.getValue().compareTo(e1.getValue());, तो आपके पास आरोही के बजाय एक अवरोही मान क्रम है।
tugcem

6
मैं res != 0 ? res : e1.getKey().compareTo(e2.getKey())समान मूल्यों के साथ कुंजियों के क्रम को संरक्षित करने के लिए वापस लौटूंगा ।
मार्को लैकोविक


17

एक TreeMapहै हमेशा कुंजी के अनुसार क्रमबद्ध, कुछ और असंभव है। एक Comparatorमात्र आपको यह नियंत्रित करने की अनुमति देता है कि चाबियाँ कैसे छांटी जाती हैं।

यदि आप सॉर्ट किए गए मान चाहते हैं, तो आपको उन्हें ए Listऔर सॉर्ट करना होगा।


10

यह एक का उपयोग करके नहीं किया जा सकता है Comparator, क्योंकि यह तुलना करने के लिए हमेशा मानचित्र की कुंजी प्राप्त करेगा । TreeMapकेवल कुंजी के आधार पर छाँट सकते हैं।


2
अगर TreeMap केवल तुलनित्र की कुंजी पास करेगा, तो क्या यह संभव होगा यदि मैं तुलनित्र के निर्माता में TreeMap का संदर्भ बनाता हूं, तो मूल्य प्राप्त करने के लिए कुंजी का उपयोग करके, कुछ इस तरह से (निश्चित नहीं कि संदर्भ से कैसे गुजरें): कक्षा में वेवल्यू तुलनित्र तुलनित्र {TreeMap theTree; public byValue (TreeMap theTree) {this.theTree = theTree; } public int तुलना (स्ट्रिंग k1, स्ट्रिंग k2) {// ट्री मैप का getKey तरीका मान पर करें}}
vito huang

1
@vito: नहीं, क्योंकि आमतौर पर दो में से एक कुंजी अभी तक नक्शे में नहीं होगी और आप इसका मूल्य प्राप्त नहीं कर पाएंगे।
जोकिम सॉयर

क्या यह TreeMap के तुलनित्र के भीतर ऐसा करने का उदाहरण नहीं है? (हालांकि, जैसा कि ऊपर उल्लेख किया गया है, यह विनिर्देशन को तोड़ता है SortedMapजिसके लिए कुंजी द्वारा छँटाई निर्दिष्ट करता है ) Beginnersbook.com/2014/07/…
मार्कस

@ मैर्कस: मानों को क्रमबद्ध करने के लिए मौजूदा मानचित्र का Comparatorउपयोग करता है। दूसरे शब्दों में, यह बाद में डाले गए मनमाने मूल्यों को नहीं छांट सकता है , केवल उन मूल्यों को जो मूल मानचित्र में पहले से हैं। TreeMap
जोआचिम सॉउर

5

ओलोफ का जवाब अच्छा है, लेकिन इसके सही होने से पहले एक और चीज की जरूरत है । उनके जवाब के नीचे की टिप्पणियों में, डकवे (सही ढंग से) बताते हैं कि उनका कार्यान्वयन सेट के लिए तुलना / बराबरी के अनुबंध का उल्लंघन करता है। यदि आप कॉल करने की कोशिश करते हैं या एक प्रविष्टि जो स्पष्ट रूप से सेट में है उस पर हटाने या हटाने की कोशिश करता है, तो सेट इसे उस कोड के कारण पहचान नहीं पाएगा जो सेट में समान मान वाले प्रविष्टियों को रखने की अनुमति देता है। इसलिए, इसे ठीक करने के लिए, हमें कुंजियों के बीच समानता के लिए परीक्षण करने की आवश्यकता है:

static <K,V extends Comparable<? super V>> SortedSet<Map.Entry<K,V>> entriesSortedByValues(Map<K,V> map) {
    SortedSet<Map.Entry<K,V>> sortedEntries = new TreeSet<Map.Entry<K,V>>(
        new Comparator<Map.Entry<K,V>>() {
            @Override public int compare(Map.Entry<K,V> e1, Map.Entry<K,V> e2) {
                int res = e1.getValue().compareTo(e2.getValue());
                if (e1.getKey().equals(e2.getKey())) {
                    return res; // Code will now handle equality properly
                } else {
                    return res != 0 ? res : 1; // While still adding all entries
                }
            }
        }
    );
    sortedEntries.addAll(map.entrySet());
    return sortedEntries;
}

"ध्यान दें कि क्रमबद्ध सेट (चाहे या नहीं, एक स्पष्ट तुलनित्र प्रदान किया गया है) द्वारा बनाए रखा क्रम बराबर होना चाहिए यदि सॉर्ट किया गया सेट इंटरफ़ेस को सही ढंग से लागू करने के लिए है ... सेट इंटरफ़ेस समान संचालन के संदर्भ में परिभाषित किया गया है , लेकिन एक सॉर्ट किया गया सेट अपनी तुलना (या तुलना) विधि का उपयोग करके सभी तत्वों की तुलना करता है, इसलिए इस विधि द्वारा बराबर समझे जाने वाले दो तत्व, सॉर्ट किए गए सेट के दृष्टिकोण से, समान हैं । " ( http://docs.oracle.com/javase/6/docs/api/java/util/SortedSet.html )

चूँकि हम मूल रूप से समान मूल्य वाली प्रविष्टियों को जोड़ने के लिए सेट को बाध्य करने के लिए समानता की अनदेखी करते थे, अब हमें सेट में वास्तव में आप जिस प्रविष्टि की तलाश कर रहे हैं उसे वापस करने के लिए कुंजियों में समानता के लिए परीक्षण करना होगा। यह थोड़े गन्दा है और निश्चित रूप से सेट का उपयोग करने का इरादा नहीं था - लेकिन यह काम करता है।


3

मुझे पता है कि यह पोस्ट विशेष रूप से मानों द्वारा एक ट्रीपॉपर सॉर्ट करने के लिए कहती है, लेकिन हममें से जो वास्तव में कार्यान्वयन के बारे में परवाह नहीं करते हैं, लेकिन एक समाधान चाहते हैं जो संग्रह को तत्वों के रूप में क्रमबद्ध रखता है, मैं इस ट्रीसेट-आधारित पर प्रतिक्रिया की सराहना करता हूं उपाय। एक के लिए, तत्वों को आसानी से कुंजी द्वारा पुनर्प्राप्त नहीं किया जाता है, लेकिन उपयोग के मामले में मेरे पास हाथ था (सबसे कम मूल्यों के साथ एन कुंजियों का पता लगाना), यह कोई आवश्यकता नहीं थी।

  TreeSet<Map.Entry<Integer, Double>> set = new TreeSet<>(new Comparator<Map.Entry<Integer, Double>>()
  {
    @Override
    public int compare(Map.Entry<Integer, Double> o1, Map.Entry<Integer, Double> o2)
    {
      int valueComparison = o1.getValue().compareTo(o2.getValue());
      return valueComparison == 0 ? o1.getKey().compareTo(o2.getKey()) : valueComparison;
    }
  });
  int key = 5;
  double value = 1.0;
  set.add(new AbstractMap.SimpleEntry<>(key, value));

2

बहुत से लोग सूची का उपयोग करने के लिए सलाह देते हैं और मैं इसे भी उपयोग करना पसंद करता हूं

यहाँ दो विधियाँ हैं जिन्हें आपको उनके मानों के अनुसार मानचित्र की प्रविष्टियों को क्रमबद्ध करना होगा।

    static final Comparator<Entry<?, Double>> DOUBLE_VALUE_COMPARATOR = 
        new Comparator<Entry<?, Double>>() {
            @Override
            public int compare(Entry<?, Double> o1, Entry<?, Double> o2) {
                return o1.getValue().compareTo(o2.getValue());
            }
        };

        static final List<Entry<?, Double>> sortHashMapByDoubleValue(HashMap temp)
        {
            Set<Entry<?, Double>> entryOfMap = temp.entrySet();

            List<Entry<?, Double>> entries = new ArrayList<Entry<?, Double>>(entryOfMap);
            Collections.sort(entries, DOUBLE_VALUE_COMPARATOR);
            return entries;
        }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.