compareTo 比较方法违反了它的一般契约

compareTo comparison method violates its general contract

我想通过 dateLastContact 比较两个 "Recipients",如果相同,则通过地址。所以这是我的代码:

public class RecipientComparator implements Comparator<Recipient> {
    @Override
    public int compare(Recipient o1, Recipient o2) {
        if (o1.isDateLastContactNull() || o2.isDateLastContactNull()) {
            if (o1.isDateLastContactNull() && o2.isDateLastContactNull()) {
                return o1.getAddress().compareTo(o2.getAddress());
            } else if (o1.isDateLastContactNull()) {
                return -1;
            } else if (o2.isDateLastContactNull()) {
                return -1;
            }
        } else {
            if (o1.getDateLastContact().equals(o2.getDateLastContact())) {
                return o1.getAddress().compareTo(o2.getAddress());
            } else
                return o1.getDateLastContact().compareTo(o2.getDateLastContact());
        }
        return 0;
    }
}

而且我总是遇到这个错误:

Exception in thread "main" java.lang.IllegalArgumentException: Comparison method violates its general contract!
    at java.util.TimSort.mergeHi(TimSort.java:899)
    at java.util.TimSort.mergeAt(TimSort.java:516)
    at java.util.TimSort.mergeCollapse(TimSort.java:439)
    at java.util.TimSort.sort(TimSort.java:245)
    at java.util.Arrays.sort(Arrays.java:1512)
    at java.util.ArrayList.sort(ArrayList.java:1462)
    at managers.RecipientManager.getAllRecipientFromAPI(RecipientManager.java:28)
    at com.company.Main.main(Main.java:18)

我已经尝试了很多东西,但现在,我不知道该怎么做。你能帮帮我吗?

收件人class:

package recipientPackage;

import paramPackage.Param;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Recipient {
    private String recipientID;
    private String address;
    private String status;
    private int contactCount;
    private Date dateLastContact;
    Param param;
    private String[] attributes = {"recipientID", "address", "status", "contactCount", "dateLastContact"};

    Recipient(String[] recipientBrut, boolean isComeFromMailing) {
        if (isComeFromMailing) {
            createRecipientMailing(recipientBrut);
        } else {
            createRecipientSurvey(recipientBrut);
        }
    }

    public void createRecipientMailing(String[] recipientBrut) {
        this.setRecipientID(recipientBrut[0].substring(recipientBrut[0].indexOf(':') + 1).replaceAll("\"", ""));
        this.setAddress(recipientBrut[1].substring(recipientBrut[1].indexOf(':') + 1).replaceAll("\"", ""));
        this.setStatus(recipientBrut[3].substring(recipientBrut[3].indexOf(':') + 1).replaceAll("\"", ""));

        try {
            this.setDateLastContactForMailing(recipientBrut[5].substring(recipientBrut[5].indexOf(':') + 1).replaceAll("\"", ""));
            this.setContactCount(Integer.parseInt(recipientBrut[4].substring(recipientBrut[4].indexOf(':') + 1).replaceAll("\"", "")));
        } catch (IndexOutOfBoundsException e) {
            this.setDateLastContactForMailing(null);
        }catch (NumberFormatException e){
            e.printStackTrace();
        }
    }

    public void createRecipientSurvey(String[] recipientBrut) {
        setAddress(recipientBrut[0]);
        setStatus(recipientBrut[1]);
        setDateLastContactForSurvey(recipientBrut[2]);
        param.setParam_point_collecte(recipientBrut[5]);
        param.setParam_langue(recipientBrut[6]);
        param.setParam_semaine(recipientBrut[7]);
        param.setParam_periode(recipientBrut[8]);
        param.setParam_envoi(recipientBrut[9]);
        param.setParam_type_visiteur(recipientBrut[10]);
        param.setParam_saison(recipientBrut[11]);
    }

    private void setDateLastContactForMailing(String dateLastContact) {
        if (dateLastContact != null) {
            SimpleDateFormat formatter = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss");
            try {
                this.dateLastContact = formatter.parse(dateLastContact);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        } else
            this.dateLastContact = null;

    }

    private void setDateLastContactForSurvey(String dateLastContact) {
        if (!dateLastContact.equals("")) {
            SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
            try {
                this.dateLastContact = formatter.parse(dateLastContact);
            } catch (ParseException ignored) {
            }
        } else
            this.dateLastContact = null;

    }
    public Boolean isDateLastContactNull() {
        return (dateLastContact == null);
    }

    public String getRecipientID() {
        return recipientID;
    }

    public void setRecipientID(String recipientID) {
        this.recipientID = recipientID;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public int getContactCount() {
        return contactCount;
    }

    public void setContactCount(int contactCount) {
        this.contactCount = contactCount;
    }

    public Date getDateLastContact() {
        return dateLastContact;
    }

    public void setDateLastContact(Date dateLastContact) {
        this.dateLastContact = dateLastContact;
    }

    public String[] getAttributes() {
        return attributes;
    }

    public void setAttributes(String[] attributes) {
        this.attributes = attributes;
    }
}

你违反了比较方法的约定,你的关系是不可传递的。 "The implementor must ensure that sgn(compare(x, y)) == -sgn(compare(y, x)) for all x and y"

考虑一下:

public static void main(String[] args){
    Recipient r1; // with isDateLastContactNull() == true;
    Recipient r2; // with isDateLastContactNull() == false;
    RecipientComparator rc = new RecipientComparator();

    System.out.println(rc.compare(r1, r2)); // -1
    System.out.println(rc.compare(r2, r1)); // -1
    // both print -1 which is not transitive.
}

原因是因为这段代码:

if (o1.isDateLastContactNull() && o2.isDateLastContactNull()) {
    // if both null, return comparison of addresses
    return o1.getAddress().compareTo(o2.getAddress());
} else if (o1.isDateLastContactNull()) {
    // if first null, return -1
    return -1;
} else if (o2.isDateLastContactNull()) {
    // if second null, also return -1 ?
    return -1; // should probably be 1 instead
}

第二个条件你应该return1


合同定义如下:

int compare(T o1, T o2):

Compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second. In the foregoing description, the notation sgn(expression) designates the mathematical signum function, which is defined to return one of -1, 0, or 1 according to whether the value of expression is negative, zero or positive.

The implementor must ensure that sgn(compare(x, y)) == -sgn(compare(y, x)) for all x and y. (This implies that compare(x, y) must throw an exception if and only if compare(y, x) throws an exception.)

The implementor must also ensure that the relation is transitive: ((compare(x, y) > 0) && (compare(y, z) > 0)) implies compare(x, z) > 0.

Finally, the implementor must ensure that compare(x, y) == 0 implies that sgn(compare(x, z)) == sgn(compare(y, z)) for all z.

It is generally the case, but not strictly required that (compare(x, y) == 0) == (x.equals(y)). Generally speaking, any comparator that violates this condition should clearly indicate this fact. The recommended language is "Note: this comparator imposes orderings that are inconsistent with equals."

一个明显的原因是:

} else if (o1.isDateLastContactNull()) {
    return -1;
} else if (o2.isDateLastContactNull()) {
    return -1;
}

或者 o1.isDateLastContactNull()true xor o2.isDateLastContactNull()true
然后 o1 < o2
您的意思是:

} else if (o1.isDateLastContactNull()) {
    return -1;
} else if (o2.isDateLastContactNull()) {
    return 1;
}