2011-11-30 10 views
123

क्या कोई मुझे सरल शब्दों में समझा सकता है, यह कोड अपवाद क्यों फेंकता है, "तुलना विधि अपने सामान्य अनुबंध का उल्लंघन करती है!", और मैं इसे कैसे ठीक करूं?"तुलना विधि अपने सामान्य अनुबंध का उल्लंघन करती है!"

private int compareParents(Foo s1, Foo s2) { 
    if (s1.getParent() == s2) return -1; 
    if (s2.getParent() == s1) return 1; 
    return 0; 
} 
+0

और अपवाद जो भी फेंक दिया गया है। –

+2

मुझे जावा या जावा तुलना API के बारे में बहुत कुछ पता नहीं है, लेकिन यह तुलना विधि गलत गलत लगता है। मान लीजिए 's1'' s2' का अभिभावक है, और 's2'' s1' का अभिभावक नहीं है। फिर 'तुलना माता-पिता (एस 1, एस 2) '''' है, लेकिन' तुलना माता-पिता (एस 2, एस 1)' ''' है। यह समझ में नहीं आता है। (इसके अलावा, यह नीचे वर्णित ऐक्स की तरह संक्रमणीय नहीं है।) – mquander

+4

यह त्रुटि केवल एक विशिष्ट लाइब्रेरी द्वारा बनाई गई प्रतीत होती है http://cr.openjdk.java.net/~martin/webrevs/openjdk7/timsort/src/share जावा में /classes/java/util/TimSort.java.html –

उत्तर

189

आपका तुलनित्र संक्रमणीय नहीं है।

Let B की मूल हो A, और BC की मूल हो। चूंकि A > B और B > C, तो यह A > C होना चाहिए। हालांकि, यदि आपका तुलनित्र A और C पर लगाया गया है, तो यह शून्य लौटाएगा, जिसका अर्थ है A == C। यह अनुबंध का उल्लंघन करता है और इसलिए अपवाद फेंकता है।

लाइब्रेरी का यह पता लगाने के लिए पुस्तकालय का अच्छा और गलत तरीके से व्यवहार करने के बजाय आपको पता है।

compareParents() में पारगमन आवश्यकता को पूरा करने का एक तरीका है केवल तत्काल पूर्वजों को देखने के बजाय getParent() श्रृंखला को पार करना।

+3

जावा 7 के 'java.util.Arrays.sort' में प्रस्तुत किया गया http://stackoverflow.com/questions/7849539/comparison-method-violates-its- सामान्य-contract-java-7- केवल – leonbloy

+11

लाइब्रेरी का पता लगाना तथ्य यह कमाल का है। सूरज में कोई भी विशालकाय फेंकने का हकदार है _ आप स्वागत करते हैं _। – Qix

+5

@Qix मुझे लगता है कि विस्मयादिबोधक बिंदु सिर्फ यही कहने का तरीका था। –

0

आप इस तरह के ऑब्जेक्ट डेटा की तुलना नहीं कर सकते: s1.getParent() == s2 - यह ऑब्जेक्ट संदर्भों की तुलना करेगा। आप फू वर्ग के लिए equals function ओवरराइड और फिर उन्हें इस s1.getParent().equals(s2)

+0

नहीं, वास्तव में मुझे लगता है कि ओपी किसी प्रकार की सूची को सॉर्ट करने की कोशिश कर रहा है, और वास्तव में संदर्भों की तुलना करना चाहता है। –

31

तरह की तुलना सिर्फ इसलिए कि यह मैं क्या हो गया, जब मैं यह त्रुटि Google पर है चाहिए, मेरी समस्या यह है कि मैं था

if (value < other.value) 
    return -1; 
else if (value >= other.value) 
    return 1; 
else 
    return 0; 

value >= other.value चाहिए (जाहिर है) वास्तव में value > other.value हो ताकि आप वास्तव में बराबर वस्तुओं के साथ 0 लौट सकें।

+5

मुझे यह जोड़ना होगा कि यदि आपका कोई भी 'मान' एक नाएन है (यदि 'मान'' डबल' या' फ्लोट' है), तो यह भी असफल हो जाएगा। – Matthieu

1

मैंने देखा है इस कोड का एक टुकड़ा जहां शून्य मान के लिए अक्सर आवर्ती जाँच प्रदर्शन किया गया था में होती हैं:

if((A==null) && (B==null) 
    return +1;//WRONG: two null values should return 0!!! 
18

अनुबंध के उल्लंघन अक्सर मतलब है कि तुलनित्र सही या संगत प्रदान नहीं कर रहा है वस्तुओं की तुलना करते समय मूल्य। उदाहरण के लिए, यदि आप एक स्ट्रिंग तुलना और साथ अंत करने के लिए सॉर्ट करने के लिए रिक्त स्ट्रिंग के लिए मजबूर प्रदर्शन करने के लिए चाहते हो सकता है:

if (one.length() == 0) { 
    return 1;     // empty string sorts last 
} 
if (two.length() == 0) { 
    return -1;     // empty string sorts last     
} 
return one.compareToIgnoreCase(two); 

लेकिन इस मामले में जहां एक और दो दोनों खाली हैं का नजारा दिखता है - और उस स्थिति में, गलत मान वापस आ गया है (एक मैच दिखाने के लिए 0 के बजाय 1), और तुलनित्र रिपोर्ट करता है कि उल्लंघन के रूप में।

if (one.length() == 0) { 
    if (two.length() == 0) { 
     return 0;    // BOth empty - so indicate 
    } 
    return 1;     // empty string sorts last 
} 
if (two.length() == 0) { 
    return -1;     // empty string sorts last     
} 
return one.compareToIgnoreCase(two); 
3

जावा एक सही अर्थों में स्थिरता की जांच नहीं करता, केवल आपको सूचित करता है अगर यह गंभीर मुसीबत में चलाता है: यह के रूप में लिखा गया है चाहिए। इसके अलावा यह आपको त्रुटि से ज्यादा जानकारी नहीं देता है।

मैं क्या मेरी सॉर्टर में हो रहा है के साथ हैरान था और एक सख्त consistencyChecker बनाया है, हो सकता है यह तुम्हारी मदद करेगा:

/** 
* @param dailyReports 
* @param comparator 
*/ 
public static <T> void checkConsitency(final List<T> dailyReports, final Comparator<T> comparator) { 
    final Map<T, List<T>> objectMapSmallerOnes = new HashMap<T, List<T>>(); 

    iterateDistinctPairs(dailyReports.iterator(), new IPairIteratorCallback<T>() { 
    /** 
    * @param o1 
    * @param o2 
    */ 
    @Override 
    public void pair(T o1, T o2) { 
     final int diff = comparator.compare(o1, o2); 
     if (diff < Compare.EQUAL) { 
     checkConsistency(objectMapSmallerOnes, o1, o2); 
     getListSafely(objectMapSmallerOnes, o2).add(o1); 
     } else if (Compare.EQUAL < diff) { 
     checkConsistency(objectMapSmallerOnes, o2, o1); 
     getListSafely(objectMapSmallerOnes, o1).add(o2); 
     } else { 
     throw new IllegalStateException("Equals not expected?"); 
     } 
    } 
    }); 
} 

/** 
* @param objectMapSmallerOnes 
* @param o1 
* @param o2 
*/ 
static <T> void checkConsistency(final Map<T, List<T>> objectMapSmallerOnes, T o1, T o2) { 
    final List<T> smallerThan = objectMapSmallerOnes.get(o1); 

    if (smallerThan != null) { 
    for (final T o : smallerThan) { 
     if (o == o2) { 
     throw new IllegalStateException(o2 + " cannot be smaller than " + o1 + " if it's supposed to be vice versa."); 
     } 
     checkConsistency(objectMapSmallerOnes, o, o2); 
    } 
    } 
} 

/** 
* @param keyMapValues 
* @param key 
* @param <Key> 
* @param <Value> 
* @return List<Value> 
*/ 
public static <Key, Value> List<Value> getListSafely(Map<Key, List<Value>> keyMapValues, Key key) { 
    List<Value> values = keyMapValues.get(key); 

    if (values == null) { 
    keyMapValues.put(key, values = new LinkedList<Value>()); 
    } 

    return values; 
} 

/** 
* @author Oku 
* 
* @param <T> 
*/ 
public interface IPairIteratorCallback<T> { 
    /** 
    * @param o1 
    * @param o2 
    */ 
    void pair(T o1, T o2); 
} 

/** 
* 
* Iterates through each distinct unordered pair formed by the elements of a given iterator 
* 
* @param it 
* @param callback 
*/ 
public static <T> void iterateDistinctPairs(final Iterator<T> it, IPairIteratorCallback<T> callback) { 
    List<T> list = Convert.toMinimumArrayList(new Iterable<T>() { 

    @Override 
    public Iterator<T> iterator() { 
     return it; 
    } 

    }); 

    for (int outerIndex = 0; outerIndex < list.size() - 1; outerIndex++) { 
    for (int innerIndex = outerIndex + 1; innerIndex < list.size(); innerIndex++) { 
     callback.pair(list.get(outerIndex), list.get(innerIndex)); 
    } 
    } 
} 
+1

यह कैसे काम करता है? आप इसका इस्तेमाल कैसे करते हैं? –

+0

बस पैरामीटर सूची और तुलनित्र के साथ चेककंसिटेंसी मेटहोड का आह्वान करें। – Martin

+0

आपका कोड संकलित नहीं करता है। कक्षाएं 'तुलना करें', 'कनवर्ट करें' (और संभावित रूप से अन्य) परिभाषित नहीं हैं। एक स्व-निहित उदाहरण के साथ कोड स्निपलेट अपडेट करें। – Gili

5

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

s1 == s2 
    return 0; 
s2 > s1 
    return 1; 
s1 < s2 
    return -1; 
6

सिद्धांत में संक्रामिता, कभी कभी सूक्ष्म कीड़े गड़बड़ बातें ... इस तरह के चल बिन्दु अंकगणितीय त्रुटि के रूप में रखती है यहां तक ​​कि अगर आपके compareTo है। यह मेरे साथ हुआ। यह मेरा कोड था:

public int compareTo(tfidfContainer compareTfidf) { 
    //descending order 
    if (this.tfidf > compareTfidf.tfidf) 
     return -1; 
    else if (this.tfidf < compareTfidf.tfidf) 
     return 1; 
    else 
     return 0; 

} 

सकर्मक संपत्ति स्पष्ट रूप से रखती है, लेकिन किसी कारण से मैं IllegalArgumentException हो रही थी। और यह पता चला है कि फ्लोटिंग पॉइंट अंकगणित में छोटी त्रुटियों के कारण, गोल-ऑफ त्रुटियां जहां ट्रांजिटिव प्रॉपर्टी को तोड़ने का कारण बनता है जहां उन्हें नहीं करना चाहिए! इसलिए मैं वास्तव में छोटे मतभेदों को 0 पर विचार करने के कोड दुबारा लिखा है, और यह काम किया:

public int compareTo(tfidfContainer compareTfidf) { 
    //descending order 
    if ((this.tfidf - compareTfidf.tfidf) < .000000001) 
     return 0; 
    if (this.tfidf > compareTfidf.tfidf) 
     return -1; 
    else if (this.tfidf < compareTfidf.tfidf) 
     return 1; 
    return 0; 
} 
0

मेरे मामले में मैं जैसे निम्नलिखित कुछ कर रहा था:

if (a.someField == null) { 
    return 1; 
} 

if (b.someField == null) { 
    return -1; 
} 

if (a.someField.equals(b.someField)) { 
    return a.someOtherField.compareTo(b.someOtherField); 
} 

return a.someField.compareTo(b.someField); 

क्या मैं जाँच करने के लिए भूल गया था, जब दोनों एक .omeField और b.someField शून्य हैं।

संबंधित मुद्दे