Search code examples
javacomparison

Java: Sorting Comparison method violates its general contract


Below is the compare method of my comparator. I am not sure what is wrong.

I would like result in below formate.

Data: 1,3,0,1,0,4,6

Result: 1,1,3,4,6,0,0

Any help will be greatly appreciated

            Comparator<Test> sortOrder = new Comparator<Test>() {
            @Override
            public int compare(Test d1, Test d2) {
                Integer o1 = new Integer(d1.getVal() == null ? 0 : d1.getVal());
                Integer o2 = new Integer(d2.getVal() == null ? 0 : d2.getVal());
                if (o1 == 0) {
                    return 1;
                }
                if (o2 == 0) {
                    return -1;
                }
                return o1.compareTo(o2);
            }
        };

        Set<Test> result = tests.stream().sorted(sortOrder).collect(Collectors.toCollection(LinkedHashSet::new));

Below is my exception:

java.lang.IllegalArgumentException: Comparison method violates its general contract!
   at java.util.TimSort.mergeLo(TimSort.java:777)
   at java.util.TimSort.mergeAt(TimSort.java:514)
   at java.util.TimSort.mergeCollapse(TimSort.java:441)
   at java.util.TimSort.sort(TimSort.java:245)
   at java.util.Arrays.sort(Arrays.java:1512)
   at java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:348)
   at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
   at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
   at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
   at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
   at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)

Solution

  • Beyond the d1.getVal() appearing twice typo (which may or may not exist in your actual code), you are violating the contract of Comparator's compare method, since sgn(compare(x,y)) != -sgn(compare(y,x)) if getVal() is 0 for both of the compared Test instances x and y.

    You should either return 0 in this case or use another field to compare the two instances.

    From the Javadoc:

    The implementor must ensure that sgn(compare(x, y)) ==-sgn(compare(y, x)) for all x and y.