Shreya Mahajan
Shreya Mahajan

Reputation: 79

How to resolve Java Collections.sort() Comparison method violates its general contract Exception

I am using Collections.sort to sort a list in ascending order based on time field. Below is the code

private String getShipmentInpickingTime(List<Shipments> shipments) {
        logger.info("in getShipmentInpickingTime");
        DateFormat sdf = new SimpleDateFormat("hh:mm");

        Collections.sort(shipments, (o1, o2) -> {
            try {
                if ((!"null".equals(o1.getShipmentinpickingtime())
                        && !StringUtils.isEmpty(o1.getShipmentinpickingtime()))
                        && (!"null".equals(o2.getShipmentinpickingtime())
                                && !StringUtils.isEmpty(o2.getShipmentinpickingtime()))) {
                    return sdf.parse(o1.getShipmentinpickingtime()).compareTo(sdf.parse(o2.getShipmentinpickingtime()));
                }
            } catch (ParseException e) {
                e.printStackTrace();
            }
            int count1 = 0;
            return count1;
        });

This method throws exception --

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

I went through the google regarding this issue- It says i am comparing bigger object to smaller object. I tried reversing the sequence of object, but no luck.

Upvotes: 0

Views: 915

Answers (2)

JollyRoger
JollyRoger

Reputation: 857

You did not mention which line of code throws this error. I am not sure which StringUtils you used here, com.sun.deploy.util.StringUtils does not have .isEmpty() method. So I reimplemented it.

Here is a working code for your problem:

public static void main(String[] args){
    getShipmentInpickingTime(new ArrayList<Shipments>(){{
        add(new Shipments("12:00"));
        add(new Shipments("12:03"));
        add(new Shipments("12:02"));
    }});
}

private static void getShipmentInpickingTime(List<Shipments> shipments) {
    DateFormat sdf = new SimpleDateFormat("hh:mm");

    System.out.println(shipments.toString());
    Collections.sort(shipments, (o1, o2) -> {
        try {
            if ((!"null".equals(o1.getShipmentPickingTime())
                    && !(o1.getShipmentPickingTime() == null || o1.getShipmentPickingTime().length() < 1))
                    && (!"null".equals(o2.getShipmentPickingTime())
                    && !(o2.getShipmentPickingTime() == null || o2.getShipmentPickingTime().length() < 1))) {
                return sdf.parse(o1.getShipmentPickingTime()).compareTo(sdf.parse(o2.getShipmentPickingTime()));
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return 0;
    });

    System.out.println(shipments.toString());
}

Shipments class content:

public class Shipments {

    private String shipmentPickingTime;

    public Shipments(String shipmentPickingTime) {
        this.shipmentPickingTime = shipmentPickingTime;
    }

    public String getShipmentPickingTime() {
        return shipmentPickingTime;
    }

    public void setShipmentPickingTime(String shipmentPickingTime) {
        this.shipmentPickingTime = shipmentPickingTime;
    }

    @Override
    public String toString() {
        return "Shipments{" +
                "shipmentPickingTime='" + shipmentPickingTime + '\'' +
                '}';
    }
}

Output:

[Shipments{shipmentPickingTime='12:00'}, Shipments{shipmentPickingTime='12:03'}, Shipments{shipmentPickingTime='12:02'}] [Shipments{shipmentPickingTime='12:00'}, Shipments{shipmentPickingTime='12:02'}, Shipments{shipmentPickingTime='12:03'}]

Upvotes: 0

Mureinik
Mureinik

Reputation: 311798

You catch the exception only once if either of the elements fail to parse.

Consider three shipments with the following times:
A - 12:34
B - 34:56
C - null

With your comparator, compare(A, C) will return 0, compare(B, C) will return 0, but compare(A, B) will return a non-zero result, thus violating the general contract of transitivity.

One easy approach is to use the Comparator.comparing syntax to parse each element separately:

DateFormat sdf = new SimpleDateFormat("hh:mm");

shipments.sort(Comparator.comparing(
                   Shipments::getShipmentinpickingtime, 
                   Comparator.nullsLast(Comparator.comparing(time -> {
                       try {
                           if (!"null".equals(time) && !StringUtils.isEmpty(time)) {
                               return sdf.parse(time);
                           }
                       } catch (ParseException ignoe) {
                           // Not a valid time
                       }
                       return null;
                   })))
              );

Upvotes: 1

Related Questions