জাভা ত্রুটি: তুলনা পদ্ধতিটি এর সাধারণ চুক্তিকে লঙ্ঘন করে


86

আমি এ সম্পর্কে অনেক প্রশ্ন দেখেছি এবং সমস্যাটি সমাধান করার চেষ্টা করেছি, তবে এক ঘন্টা গুগল করার পরে এবং প্রচুর পরীক্ষার এবং ত্রুটির পরেও আমি এটিকে ঠিক করতে পারছি না। আমি আশা করি আপনারা কেউ কেউ সমস্যাটি দেখেছেন।

এটি আমি পেয়েছি:

java.lang.IllegalArgumentException: Comparison method violates its general contract!
    at java.util.ComparableTimSort.mergeHi(ComparableTimSort.java:835)
    at java.util.ComparableTimSort.mergeAt(ComparableTimSort.java:453)
    at java.util.ComparableTimSort.mergeForceCollapse(ComparableTimSort.java:392)
    at java.util.ComparableTimSort.sort(ComparableTimSort.java:191)
    at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
    at java.util.Arrays.sort(Arrays.java:472)
    at java.util.Collections.sort(Collections.java:155)
    ...

এবং এটি আমার তুলনাকারী:

@Override
public int compareTo(Object o) {
    if(this == o){
        return 0;
    }

    CollectionItem item = (CollectionItem) o;

    Card card1 = CardCache.getInstance().getCard(cardId);
    Card card2 = CardCache.getInstance().getCard(item.getCardId());

    if (card1.getSet() < card2.getSet()) {
        return -1;
    } else {
        if (card1.getSet() == card2.getSet()) {
            if (card1.getRarity() < card2.getRarity()) {
                return 1;
            } else {
                if (card1.getId() == card2.getId()) {
                    if (cardType > item.getCardType()) {
                        return 1;
                    } else {
                        if (cardType == item.getCardType()) {
                            return 0;
                        }
                        return -1;
                    }
                }
                return -1;
            }
        }
        return 1;
    }
}

কোন ধারণা?


কোডটির কোন রেখার কারণে এই ব্যতিক্রমটি নিক্ষেপ করা হচ্ছে? তুলনাযোগ্য টিমসোর্ট.জভা এর 835 এবং 453 লাইনে কী আছে?
ইওলসের পূর্ণ

আমার কাছে মনে হচ্ছে আপনার এই পদ্ধতিটি Cardশ্রেণীর প্রতিনিধি হওয়া উচিত : return card1.compareTo(card2)এবং এই যুক্তিটি সেখানে প্রয়োগ করুন।
হান্টার ম্যাকমিলেন

4
@ হোভারক্র্যাফটফুলঅফিলস এটি ওরাকল থেকে একটি ক্লাস, আমার দ্বারা বিরক্ত না। এটি এই লাইনে একটি ব্যতিক্রম ছুঁড়েছে। পদ্ধতিটি খুব দীর্ঘ এবং বুঝতে খুব কঠিন দেখাচ্ছে।
লাকাতোস গায়ুলা

পুনঃটুইট কার্ডে কেবলমাত্র কোনও কার্ডের স্ট্যাটিক ডেটা থাকে (নাম, পাঠ্য ইত্যাদি ...) যখন এই শ্রেণিতে কিছু পরিবর্তনশীল ভেরিয়েবল যেমন টাইপ এবং পরিমাণ থাকে।
লাকাতোস গায়ুলা

4
ক্লিন কোড বইটি পড়ার পরে আমিও জানি না।
Lakatos Gyula

উত্তর:


99

ব্যতিক্রম বার্তাটি আসলে বেশ বর্ণনামূলক। চুক্তি এটা উল্লেখ করা হয় transitivity যদি A > Bএবং B > Cতারপর কোনো A, Bএবং C: A > C। আমি এটি কাগজ এবং পেন্সিল দিয়ে পরীক্ষা করেছি এবং আপনার কোডটিতে কয়েকটি গর্ত আছে বলে মনে হচ্ছে:

if (card1.getRarity() < card2.getRarity()) {
  return 1;

আপনি -1যদি ফিরে না card1.getRarity() > card2.getRarity()


if (card1.getId() == card2.getId()) {
  //...
}
return -1;

-1আইডি সমান না হলে আপনি ফিরে আসবেন । আপনার ফিরে আসা উচিত -1বা 1কোন আইডি বড় ছিল তার উপর নির্ভর করে।


এক নজর দেখে নাও. অনেক বেশি পঠনযোগ্য হওয়া ছাড়াও আমার মনে হয় এটি আসলে কাজ করা উচিত:

if (card1.getSet() > card2.getSet()) {
    return 1;
}
if (card1.getSet() < card2.getSet()) {
    return -1;
};
if (card1.getRarity() < card2.getRarity()) {
    return 1;
}
if (card1.getRarity() > card2.getRarity()) {
    return -1;
}
if (card1.getId() > card2.getId()) {
    return 1;
}
if (card1.getId() < card2.getId()) {
    return -1;
}
return cardType - item.getCardType();  //watch out for overflow!

15
প্রকৃতপক্ষে, আপনি যে উদাহরণ দিয়েছেন তা ট্রানজিটিভিটি লঙ্ঘন করে না, তবে ডকুম.অরাকল . com/javase/7/docs/ এ বর্ণিত sgn (তুলনা (x, y)) == -sgn (তুলনা (y, x)) এপিআই / জাভা / ইউজার /… - এটি গাণিতিক দিক থেকে বিপরীতমুখী।
হ্যান্স-পিটার স্টার

4
আমি if (card1.getSet() != card2.getSet()) return card1.getSet() > card2.getSet() ? 1 : -1;যদি কেউ যত্নশীল হয় তবে আরও গতির জন্য ব্যবহার করার পরামর্শ দেব ।
মার্টিনাস

আমি এটির মুখোমুখি হয়েছি এবং এটি আমার তুলনাকারীর একটি প্রতিসম সমস্যা ছিল । নির্ণয়ের শক্ত অংশটি হ'ল আমার দুর্বলতাটি প্রথম ক্ষেত্র ছিল (একটি বুলিয়ান ক্ষেত্র চেক, একটি সঠিক তুলনা নয়) যা উভয়ই সঠিক হওয়ার জন্য যাচাই করে নি। এটি কেবল তখনই উন্মোচিত হয়েছিল, আমি পরে পরবর্তী অধস্তন তুলনা যুক্ত করার পরে যা অবশ্যই বাছাইয়ের ক্রমটি বিভ্রান্ত করেছে।
থমাস ডাব্লু

4
আপনার পরামর্শের জন্য @ maartartus ধন্যবাদ! এটি আমার জন্য নিখুঁত ছিল :)
সোনহজা 29:17

46

আপনি আপনার তুলকগুলিতে ট্রান্সজিটিশন বাগগুলি চিহ্নিত করতে নিম্নলিখিত শ্রেণীটি ব্যবহার করতে পারেন:

/**
 * @author Gili Tzabari
 */
public final class Comparators
{
    /**
     * Verify that a comparator is transitive.
     *
     * @param <T>        the type being compared
     * @param comparator the comparator to test
     * @param elements   the elements to test against
     * @throws AssertionError if the comparator is not transitive
     */
    public static <T> void verifyTransitivity(Comparator<T> comparator, Collection<T> elements)
    {
        for (T first: elements)
        {
            for (T second: elements)
            {
                int result1 = comparator.compare(first, second);
                int result2 = comparator.compare(second, first);
                if (result1 != -result2)
                {
                    // Uncomment the following line to step through the failed case
                    //comparator.compare(first, second);
                    throw new AssertionError("compare(" + first + ", " + second + ") == " + result1 +
                        " but swapping the parameters returns " + result2);
                }
            }
        }
        for (T first: elements)
        {
            for (T second: elements)
            {
                int firstGreaterThanSecond = comparator.compare(first, second);
                if (firstGreaterThanSecond <= 0)
                    continue;
                for (T third: elements)
                {
                    int secondGreaterThanThird = comparator.compare(second, third);
                    if (secondGreaterThanThird <= 0)
                        continue;
                    int firstGreaterThanThird = comparator.compare(first, third);
                    if (firstGreaterThanThird <= 0)
                    {
                        // Uncomment the following line to step through the failed case
                        //comparator.compare(first, third);
                        throw new AssertionError("compare(" + first + ", " + second + ") > 0, " +
                            "compare(" + second + ", " + third + ") > 0, but compare(" + first + ", " + third + ") == " +
                            firstGreaterThanThird);
                    }
                }
            }
        }
    }

    /**
     * Prevent construction.
     */
    private Comparators()
    {
    }
}

Comparators.verifyTransitivity(myComparator, myCollection)ব্যর্থ হওয়া কোডটির সামনে কেবল অনুরোধ করুন ।


4
এই কোডটি তুলনা (a, খ) = - তুলনা (খ, গ) কে বৈধতা দেয়। এটি যাচাই করে না যে যদি (ক, খ)> ০ && তুলনা (খ, সি)> ০ হয় তবে তুলনা করুন (ক, সি)> ০
ওলাফ আছোভেন

4
আমি সত্যিই আপনার ক্লাসটি খুব, খুব দরকারী খুঁজে পেয়েছি। ধন্যবাদ
ক্রিগোর

আপনাকে ধন্যবাদ, আমাকে ফিরে আসতে হবে এবং উত্তরটি উপবিষ্ট করতে হয়েছিল কারণ তুলনামূলক ডিবাগ করার সময় এই শ্রেণিটি খুব দরকারী কারণ কেবল এখানে উল্লিখিত উদ্দেশ্যে নয় কারণ এটি অন্যান্য পরিস্থিতিতেও পরীক্ষার জন্য পরিবর্তন করা যেতে পারে!
জ্যাকো-বেন ভসলু

37

এটির জেডিকে সংস্করণে কিছু করার আছে। যদি এটি জেডিকে in তে ভাল করে, সম্ভবত আপনার দ্বারা বর্ণিত জেডিকে K এ এটির সমস্যা হতে পারে, কারণ জেডিকে in এর বাস্তবায়ন পদ্ধতি পরিবর্তন করা হয়েছে।

এটা দেখ:

বর্ণনা: java.util.Arrays.sortএবং (পরোক্ষভাবে) দ্বারা ব্যবহৃত বাছাই করা অ্যালগরিদম java.util.Collections.sortপ্রতিস্থাপন করা হয়েছে। চুক্তি লঙ্ঘন করে এমন IllegalArgumentExceptionএকটি সনাক্ত করে যদি নতুন সাজানোর বাস্তবায়ন এটি ফেলে দিতে পারে । আগের বাস্তবায়ন নীরবে এইরকম পরিস্থিতি উপেক্ষা করেছিল। পূর্ববর্তী আচরণটি যদি পছন্দসই হয় তবে আপনি নতুন সিস্টেমের সম্পত্তিটি ব্যবহার করতে পারেন,ComparableComparablejava.util.Arrays.useLegacyMergeSort পূর্বের একীকরণ আচরণটি পুনরুদ্ধার করতে ।

সঠিক কারণ আমি জানি না। তবে, আপনি সাজানোর আগে কোড যুক্ত করুন। ইহা ঠিক হবে.

System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");

4
সমস্যাটি যুক্তিগুলিতে ছিল আমি জিনিসগুলির তুলনা করতাম। আমি এটিকে উন্নত করব, আশা করি এটি এমন কাউকে সহায়তা করতে পারে যারা এই ধরণের সমস্যা খুঁজছেন।
লাকাতোস গায়ুলা

10
এটি কেবল তুলনামূলক ঠিক না করা পর্যন্ত জিনিসগুলিকে আবার কাজ করতে কেবল দ্রুত এবং কুৎসিত কাজ হিসাবে ব্যবহার করা উচিত। যদি ব্যতিক্রম ঘটে থাকে এর অর্থ হল আপনার তুলনাকারী বগি এবং সাজানোর ক্রমটি অদ্ভুত হবে। আপনাকে ডকস.অরাকল . com/ জাভাসে / //docs/api/java/util/… প্রদত্ত সমস্ত বিধি বিবেচনা করতে হবে ।
হ্যান্স-পিটার স্টার

"অদ্ভুত" যদি আমি যাচ্ছিলাম তবে কী হবে? (a,b) -> { return (new int[]{-1,1})[(int)((Math.random()*2)%2)]}আমি জানি যে Collections.shuffleবিদ্যমান, তবে আমি অতিরিক্ত সুরক্ষিত হওয়া পছন্দ করি না ।
জেফারি গুহ

আমার একটি পুরানো অ্যাপ্লিকেশন রয়েছে এবং জেডিকে 8 এর সাথে আমি এই ত্রুটিটি পেয়েছি। JDK6 ব্যবহার করে এটি সঠিকভাবে কাজ করে, তাই আমি কেবল 6 এ ডাউনগ্রেড করি, ধন্যবাদ।
স্টেফানো স্কর্পান্তি

6

নিম্নলিখিত বিষয় বিবেচনা করুন:

প্রথম, o1.compareTo(o2)বলা হয়। card1.getSet() == card2.getSet()সত্য হতে পারে এবং ঠিক তাই হয়card1.getRarity() < card2.getRarity() , তাই আপনি 1 ফিরে।

তারপরে, o2.compareTo(o1)বলা হয়। আবার, card1.getSet() == card2.getSet()সত্য। তারপরে, আপনি নিম্নলিখিতটি এড়িয়ে যান else, তারপরে card1.getId() == card2.getId()সত্য হয়ে যায় এবং ঠিক তাই হয়cardType > item.getCardType() । আপনি আবার 1 ফিরে।

যে থেকে o1 > o2, এবং o2 > o1। আপনি চুক্তিটি ভঙ্গ করেছেন।


2
        if (card1.getRarity() < card2.getRarity()) {
            return 1;

তবে এর card2.getRarity()চেয়ে কম যদি card1.getRarity()আপনি ফিরে নাও পারেন -1

আপনি একইভাবে অন্যান্য ক্ষেত্রে মিস। আমি এটি করব, আপনার অভিপ্রায় অনুসারে আপনি প্রায় পরিবর্তন করতে পারেন:

public int compareTo(Object o) {    
    if(this == o){
        return 0;
    }

    CollectionItem item = (CollectionItem) o;

    Card card1 = CardCache.getInstance().getCard(cardId);
    Card card2 = CardCache.getInstance().getCard(item.getCardId());
    int comp=card1.getSet() - card2.getSet();
    if (comp!=0){
        return comp;
    }
    comp=card1.getRarity() - card2.getRarity();
    if (comp!=0){
        return comp;
    }
    comp=card1.getSet() - card2.getSet();
    if (comp!=0){
        return comp;
    }   
    comp=card1.getId() - card2.getId();
    if (comp!=0){
        return comp;
    }   
    comp=card1.getCardType() - card2.getCardType();

    return comp;

    }
}

1

এটি ওপেনজেডকে বাগও হতে পারে ... (এই ক্ষেত্রে নয় তবে এটি একই ত্রুটি)

আমার মত কেউ যদি এই উত্তর সম্পর্কে হোঁচট খায়

java.lang.IllegalArgumentException: Comparison method violates its general contract!

তাহলে এটি জাভা-সংস্করণে একটি বাগও হতে পারে। আমার বেশ কয়েকটি বছর ধরে এখন কিছু অ্যাপ্লিকেশনে তুলনা চলছে। তবে হঠাৎ করে এটি কাজ করা বন্ধ করে দেয় এবং সমস্ত তুলনা শেষ হওয়ার পরে ত্রুটি ছুঁড়ে দেয় (আমি "0" ফেরার আগে 6 টি বৈশিষ্ট্যের তুলনা করি)।

এখন আমি সবেমাত্র ওপেনজেডিকে এই বুগেরপোর্টটি পেয়েছি:


1

আমারও একই লক্ষণ ছিল। আমার জন্য এটি প্রমাণিত হয়েছে যে কোনও স্ট্রিমে বাছাইয়ের সময় অন্য থ্রেড তুলনা করা অবজেক্টগুলিকে সংশোধন করছে। সমস্যাটি সমাধান করার জন্য, আমি অবজেক্ট অস্থায়ী বস্তুগুলিতে বস্তুগুলি ম্যাপ করেছি, স্ট্রিমটিকে একটি অস্থায়ী সংগ্রহে সংগ্রহ করেছি এবং সেগুলি অনুসারে বাছাই করেছি।


0

আমাকে বেশ কয়েকটি মানদণ্ড বাছাই করতে হয়েছিল (তারিখ, এবং একই তারিখ যদি; অন্যান্য জিনিস ...)। জাভার পুরানো সংস্করণ সহ এক্লিপসে যা কাজ করা হয়েছিল, অ্যান্ড্রয়েডে আর কোনও কাজ হয়নি: তুলনা পদ্ধতি চুক্তি লঙ্ঘন করে ...

স্ট্যাকওভারফ্লোতে পড়ার পরে, আমি পৃথক ফাংশন লিখেছিলাম যা তারিখগুলি একই হলে তুলনা () থেকে কল করেছিলাম। এই ফাংশনটি মানদণ্ড অনুযায়ী অগ্রাধিকার গণনা করে এবং () তুলনা করতে -1, 0, বা 1 প্রদান করে। এটা এখন কাজ মনে হচ্ছে।


0

নিম্নলিখিত শ্রেণীর মতো আমিও একই ত্রুটি পেয়েছি StockPickBean। এই কোড থেকে কল করা:

List<StockPickBean> beansListcatMap.getValue();
beansList.sort(StockPickBean.Comparators.VALUE);

public class StockPickBean implements Comparable<StockPickBean> {
    private double value;
    public double getValue() { return value; }
    public void setValue(double value) { this.value = value; }

    @Override
    public int compareTo(StockPickBean view) {
        return Comparators.VALUE.compare(this,view); //return 
        Comparators.SYMBOL.compare(this,view);
    }

    public static class Comparators {
        public static Comparator<StockPickBean> VALUE = (val1, val2) -> 
(int) 
         (val1.value - val2.value);
    }
}

একই ত্রুটি পাওয়ার পরে:

java.lang.IllegalArgumentException: তুলনা পদ্ধতিটি এর সাধারণ চুক্তি লঙ্ঘন করে!

আমি এই লাইনটি পরিবর্তন করেছি:

public static Comparator<StockPickBean> VALUE = (val1, val2) -> (int) 
         (val1.value - val2.value);

প্রতি:

public static Comparator<StockPickBean> VALUE = (StockPickBean spb1, 
StockPickBean spb2) -> Double.compare(spb2.value,spb1.value);

এটি ত্রুটি সংশোধন করে।


0

আমি একই ধরণের সমস্যায় পড়েছি যেখানে আমি এমন একটি n x 2 2D arrayনাম বাছাই করার চেষ্টা করছিলাম contestsযা সাধারণ পূর্ণসংখ্যার 2D অ্যারে। এটি বেশিরভাগ সময় ধরে কাজ করে যাচ্ছিল তবে একটি ইনপুটটির জন্য রানটাইম ত্রুটি নিক্ষেপ করেছে: -

Arrays.sort(contests, (row1, row2) -> {
            if (row1[0] < row2[0]) {
                return 1;
            } else return -1;
        });

ত্রুটি:-

Exception in thread "main" java.lang.IllegalArgumentException: Comparison method violates its general contract!
    at java.base/java.util.TimSort.mergeHi(TimSort.java:903)
    at java.base/java.util.TimSort.mergeAt(TimSort.java:520)
    at java.base/java.util.TimSort.mergeForceCollapse(TimSort.java:461)
    at java.base/java.util.TimSort.sort(TimSort.java:254)
    at java.base/java.util.Arrays.sort(Arrays.java:1441)
    at com.hackerrank.Solution.luckBalance(Solution.java:15)
    at com.hackerrank.Solution.main(Solution.java:49)

উপরের উত্তরগুলির দিকে তাকিয়ে আমি এর জন্য একটি শর্ত যুক্ত করার চেষ্টা equalsকরেছি এবং কেন এটি কাজ করেছিল তা আমি জানি না। আশা করি আমাদের অবশ্যই সমস্ত ক্ষেত্রে (এর চেয়ে বড়, সমান এবং এর চেয়ে কম) কী ফিরিয়ে আনতে হবে তা স্পষ্টভাবে উল্লেখ করতে হবে:

        Arrays.sort(contests, (row1, row2) -> {
            if (row1[0] < row2[0]) {
                return 1;
            }
            if(row1[0] == row2[0]) return 0;
            return -1;
        });
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.