"तुलना विधि अपने सामान्य अनुबंध का उल्लंघन करती है!"


187

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

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

1
अपवाद का नाम और वर्ग क्या है? क्या यह एक IllegalArgumentException है? अगर मुझे अंदाजा होता तो मुझे लगता था कि आपको s1.getParent().equals(s2)इसके बजाय करना चाहिए s1.getParent() == s2
Freiheit

और अपवाद जो फेंक दिया जाता है।
मैथ्यू फरवेल

2
मुझे जावा या जावा तुलना एपीआई के बारे में ज्यादा जानकारी नहीं है, लेकिन यह तुलना विधि गलत है। मान लीजिए s1कि यह माता-पिता है s2, और s2इसका जनक नहीं है s1। तब compareParents(s1, s2)है 0, लेकिन compareParents(s2, s1)है 1। इसका कोई मतलब नहीं है। (इसके अलावा, यह सकर्मक नहीं है, जैसा कि नीचे उल्लेख किए गए ऐक्स की तरह है।)
मकुप

4
यह त्रुटि केवल एक विशिष्ट पुस्तकालय cr.openjdk.java.net/~martin/webrevs/openjdk7/timsort/src/share/…
पीटर लॉरी

जावा में, आप बराबरी का उपयोग कर सकते हैं (एक बूलियन लौटाएं) या तुलना करें (-1, 0 या +1 लौटें)। अपने फू कक्षा में इस कार्य को ओवरराइड करें और उसके बाद, आप s1.getParent () की बराबरी कर सकते हैं। (s2) ...
Mualig

जवाबों:


261

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

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

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

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


3
में जावा 7 के शुरू java.util.Arrays.sort stackoverflow.com/questions/7849539/...
leonbloy

46
इस तथ्य का पता लगा रहा है कि पुस्तकालय बहुत बढ़िया है। सूरज में कोई आपका स्वागत करने के लिए एक विशाल को बाहर फेंकने का हकदार है ।
Qix - MONICA WAS MISTREATED

क्या आप इस प्रश्न को संदर्भ पद के रूप में अधिक उपयोगी बनाने के लिए इस उत्तर को थोड़ा सामान्य कर सकते हैं?
बर्नहार्ड बार्कर

1
@ क्यूक्स - जितना मैं सूरज से प्यार करता हूं, यह जावा 7 को ओरेकल बैनर के तहत जोड़ा गया था
isapir

1
@isapir लानत है! अच्छी पकड़।
Qix - मोनासा

38

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

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

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


7
मैं जोड़ने के लिए है कि यदि आपके से कोई भी valueएक NaN है (अगर valueएक doubleया float), यह रूप में अच्छी तरह विफल हो जाएगा।
मत्तीव्यू

22

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

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

लेकिन यह उस मामले को नजरअंदाज करता है जहां दोनों एक और दो खाली हैं - और उस मामले में, गलत मान लौटाया जाता है (1 को दिखाने के लिए 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 );

13

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

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;

}   

सकर्मक संपत्ति स्पष्ट रूप से रखती है, लेकिन किसी कारण के लिए मैं अवैध तरीके से हो रहा था। और यह पता चला है कि फ्लोटिंग पॉइंट अंकगणित में छोटी-छोटी त्रुटियों के कारण, राउंड-ऑफ त्रुटियां जहां संक्रमणकारी संपत्ति को तोड़ने का कारण बन सकती है जहां आपको नहीं होना चाहिए! इसलिए मैंने कोड को वास्तव में छोटे अंतर 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;
}   

2
यह मददगार था! मेरा कोड तार्किक रूप से ठीक था लेकिन सटीक समस्या के कारण कोई त्रुटि थी।
JSong

6

हमारे मामले में यह त्रुटि हो रही थी क्योंकि हमने s1 और s2 की तुलना के क्रम में गलती से फ़्लिप किया था। तो उसके लिए बाहर देखो। यह स्पष्ट रूप से निम्नलिखित की तुलना में अधिक जटिल था लेकिन यह एक दृष्टांत है:

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

3

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

मुझे इस बात से घबराहट हुई कि मेरे सॉर्टर में क्या हो रहा है और एक सख्त संगतकार बना दिया है, शायद इससे आपको मदद मिलेगी:

/**
 * @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));
    }
  }
}

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

आपका कोड संकलित नहीं करता है। कक्षाएं Compare, Convert(और संभवतः अन्य) परिभाषित नहीं हैं। कृपया आत्म-निहित उदाहरण के साथ कोड स्निपलेट को अपडेट करें।
गिली

कोड को अधिक पठनीय बनाने के लिए आपको टाइपो को ठीक करना चाहिए checkConsi(s)tencyऔर सभी अनावश्यक @paramघोषणाओं को हटा देना चाहिए ।
रोलैंड इलिग

3

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

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

जब मैं a.someField और b.someField दोनों शून्य था, तो मैं जांच करना भूल गया था।


3

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

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

2

तो compareParents(s1, s2) == -1फिर compareParents(s2, s1) == 1उम्मीद है। आपके कोड के साथ यह हमेशा सच नहीं होता है।

विशेष रूप से अगर s1.getParent() == s2 && s2.getParent() == s1। यह संभावित समस्याओं में से एक है।


1

VM कॉन्फ़िगरेशन का संपादन मेरे लिए काम करता है।

-Djava.util.Arrays.useLegacyMergeSort=true

कृपया दोहराएं कि प्रारूपण में आपकी सहायता करने का मेरा प्रयास कुछ भी नहीं तोड़ता है। मैं -प्रस्तावित समाधान की शुरुआत के बारे में अनिश्चित हूं । हो सकता है कि आपने इसके बदले एक-आइटम बुलेट बिंदु सूची जैसी किसी चीज़ का इरादा किया हो।
युननॉश

2
कृपया यह भी बताएं कि यह वर्णित समस्या के साथ कैसे मदद करता है। वर्तमान में यह व्यावहारिक रूप से एक कोड-ओनली उत्तर है।
युननॉश

0

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


नहीं, वास्तव में मुझे लगता है कि ओपी किसी प्रकार की एक सूची को क्रमबद्ध करने की कोशिश कर रहा है, और वास्तव में संदर्भों की तुलना करना चाहता है।
एडवर्ड फॉक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.