4

I have a list of Cars scheduled for delivery for multiple dates which needs to be sorted on the basis of the below points:

  • If isReady>0, then it should be displayed first in the table. And then the other values come below it for that particular date.
  • If isReady>0 and Object gear!=null then, it is displayed first in the table for that particular date. Followed by the other values where Object gear==null.
  • If isReady>0, Object gear!=null and Object tyre!=null, then that value is displayed first in the table for that particular date. Followed by the other values where Object gear==null and tyre==null.

Here are the class codes:

public class Car {
    private int isReady;
    private Tyre tyre;
    private Gear gear;
    private Date deliveryDate;
}


public class Gear {
    private int id;
    private String type;
}


public class Tyre {
    private int id;
    private String grip;
}

public class CarComparator implements Comparator<Car> {
    @Override
    public int compare(Car entry1, Car entry2) {
        int value = 0;

        if (entry1.getIsReady() > entry2.getIsReady()) {
            value = -1;
        } else if (entry1.getIsReady() < entry2.getIsReady()) {
            value = 1;
        } else if (entry1.getIsReady() == entry2.getIsReady()) {
            value = 0;
        }
        return value;
    }
}

I have developed a Comparator which works fine for the first condition where isReady>0. Could you please help me with the other conditions mentioned above.

Thanks in advance.

10
  • 1
    In your current code, if value is not 0 at the end, you can return it. But if it is 0, you need to then compare the next condition. And so on. Commented Oct 10, 2019 at 6:56
  • Side note: your current comparator could be simplified to int value =Integer.compare(entry2.getIsReady(), entry1.getIsReady() );. Commented Oct 10, 2019 at 6:57
  • When my value=0, i need to make a function eg compareTyre() and check my condition inside it? Commented Oct 10, 2019 at 6:57
  • @champ.exe you don't necessarily need to make it a separate method, but you can. But if value is 0 and you still have something to compare, you need to do more comparisons. Commented Oct 10, 2019 at 7:00
  • Your requirement for isReady>0 doesn't match your comparator. To clarify: you have all cars in a list and want to sort them 1) by date in ascending order, 2) by isReady in ascending order, 3) non-null values first for gear and 4) non-null values first for tyre - is that correct? How should cars with the same date, isReady a gear and a tyre be sorted? Does it matter? Commented Oct 10, 2019 at 7:01

4 Answers 4

4

Well, as of Java 8 you could build your comparator like this:

//order by delivery date first, ascending order
Comparator<Car> carComparator = Comparator.comparing( Car::getDeliveryDate )
  //order by isReady in ascending order
  .thenComparing( Car::getIsReady )
  //we map null to 1 and non-null to -1 and ignore the rest for now
  .thenComparing( car -> car.getGear() != null ? -1 : 1 ) 
  .thenComparing( car -> car.getTyre() != null ? -1 : 1 );
Sign up to request clarification or add additional context in comments.

Comments

3

Check this comparator so you can sort with multiple attributes

public class CarComparator implements Comparator<Car> {

    @Override
    public int compare(Car entry1, Car entry2) {
        int value;
        if (entry1.getDeliveryDate().before(entry2.getDeliveryDate())){
            value = -1;
        }else if (entry1.getDeliveryDate().equals(entry2.getDeliveryDate())){
            value = 0;
        }else{
            value =1;
        }
        //For same day
        if (value==0){
            if (entry1.getIsReady() > entry2.getIsReady()) {
                value = -1;
            } else if (entry1.getIsReady() < entry2.getIsReady()) {
                value = 1;
            } else if (entry1.getIsReady() == entry2.getIsReady()) {
                value = 0;
            }
        }
        //if same isReady
        if (value==0){
            if (entry1.getGear()!=null && entry2.getGear()==null) {
                value = -1;
            } else  if (entry1.getGear()==null && entry2.getGear()==null) {
                value = 0;
            } else{
                value = 1;
            }
        }
        //if still equals
        if (value==0){
            if (entry1.getTyre()!=null && entry2.getTyre()==null) {
                value = -1;
            } else  if (entry1.getTyre()==null && entry2.getTyre()==null) {
                value = 0;
            } else{
                value = 1;
            }
        }


        return value;
    }
}

I am not sure if this is what you try to do. What the above comparator does is: First to sort with Dates, if it finds equal dates (value=0), it compares the isReady, then getGear() and finally the getTyre().

That way you can add as many attributes as you need in your comparator.

Including the main method with 3 cars

public class Main {
    public static void main (String[]args) throws UnsupportedEncodingException, ParseException {

        List<Car> carL = new ArrayList<Car>();

        Car car1 = new Car();
        car1.setDeliveryDate(new Date());
        Gear gear1 = new Gear();
        car1.setGear(gear1);
        Tyre tyre1 = new Tyre();
        car1.setTyre(null);
        car1.setId(1);
        car1.setDeliveryDate((new SimpleDateFormat("dd-MM-yyyy")).parse("01-01-2000"));
        car1.setIsReady(0);

        Car car2 = new Car();
        car2.setDeliveryDate(new Date());
        Gear gear2 = new Gear();
        car2.setGear(gear2);
        Tyre tyre2 = new Tyre();
        car2.setTyre(tyre2);
        car2.setId(2);
        car2.setDeliveryDate((new SimpleDateFormat("dd-MM-yyyy")).parse("02-01-2000"));

        car2.setIsReady(1);

        Car car3 = new Car();
        car3.setDeliveryDate(new Date());
        Gear gear3 = new Gear();
        car3.setGear(gear3);
        Tyre tyre3 = new Tyre();
        car3.setTyre(tyre3);
        car3.setId(3);
        car3.setDeliveryDate((new SimpleDateFormat("dd-MM-yyyy")).parse("01-01-2000"));

        car3.setIsReady(1);

        carL.add(car1);
        carL.add(car2);
        carL.add(car3);
        Collections.sort(carL, new CarComparator());
        for (Car car : carL) {
            System.out.println("car: " + car.toString());
        }
    }
}

outputs:

car: Car{id=3, isReady=1, tyre=false, gear=false, deliveryDate=Sat Jan 01 00:00:00 EET 2000}
car: Car{id=1, isReady=0, tyre=true, gear=false, deliveryDate=Sat Jan 01 00:00:00 EET 2000}
car: Car{id=2, isReady=1, tyre=false, gear=false, deliveryDate=Sun Jan 02 00:00:00 EET 2000}

Comments

0

To be honest I don't see anything wrong with your code. That is if your intention is to return a -1 when entry1 is larger than entry 2(reverse order of the norm). Without further code to read, I think your code will work if you are trying to compare which value is larger or smaller that is built for your usage case.

However, I think there is some inefficiency in your returning method. You don't need to return value. You can just return an actual value.

For comparison, you can just compare == first then evaluate the rest after. But that might be hard to read the code so I give you two versions.

Remove value version:

public class CarComparator implements Comparator<Car> {
    @Override
    public int compare(Car entry1, Car entry2) {
        if (entry1.getisReady() > entry2.getisReady()) {
            return -1;
        } else if (entry1.getisReady() < entry2.getisReady()) {
            return 1;
        } else if (entry1.getisReady() == entry2.getisReady()) {
            return 0;
        }
    }
}

Remove value and different style of comparison:

public class CarComparator implements Comparator<Car> {

    @Override
    public int compare(Car entry1, Car entry2) {
        if (entry1.getisReady() == entry2.getisReady()) return 0;            
        return entry1.getisReady() > entry2.getisReady()? -1 : 1;
    }
}

I am not sure if I got you correctly but if I don't, I hope this code can help you. I separated them into 3 methods and I hope you got a getTyre() and getGear() method. You can combine them as needed and for the last method, its values were separated into variable order for easy reading of code.

class CarComparator implements Comparator<Car> {

    public int compare(Car entry1, Car entry2) {
        if (entry1.getisReady() == entry2.getisReady()) return 0;
        return entry1.getisReady() > entry2.getisReady()? -1 : 1;
    }

    public int compareGear(Car entry1, Car entry2){
        if ( (entry1.getGear() != null && entry2.getGear() != null) 
           ||(entry1.getGear() == null && entry2.getGear() == null)
           ){
            return compare(entry1, entry2);
        }  
        return entry1.getGear() != null && entry2.getGear() == null? -1 : 1;

    }

    public int compareTye(Car entry1, Car entry2){
        int order1 = entry1.getGear() != null && entry1.getTyre() != null? 1 : 0;
        int order2 = entry2.getGear() != null && entry2.getTyre() != null? 1 : 0;

        if ( order1 == order2 ) return compare(entry1, entry2);
        return order1 > order2? -1 : 1;
    } 
}

3 Comments

thanks for the simplification. My code works correctly, but I need help for the nested sorting of objects. That is for tyre and gear .
You are welcome. I am not sure if I got you correctly, but I updated the answer. I hope that helps. You can always combine those methods. You can also write tyre so that compareTyre call compareGear() then compareGear call compare or the other way around.
Thanks, i will try to execute this and see if it works.
0

why not reuse Integer.compareTo to make the code shorter?

like this:

import java.util.Comparator;

public class CarComparator implements Comparator<Car> {
    @Override
    public int compare(Car entry1, Car entry2) {
        int value = 0;

        // might want to add a null check for either entry1 and entry2

        value = entry1.getDeliveryDate().compareTo(entry2.getDeliveryDate());
        if (value == 0) {
            value = ((Integer)entry1.getIsReady()).compareTo((Integer)entry2.getIsReady());
            if (value == 0) {
                value = getIntegerValueForNullCheck(entry1.getGear()).compareTo(getIntegerValueForNullCheck(entry2.getGear()));
                if (value == 0) {
                    value = getIntegerValueForNullCheck(entry1.getTyre()).compareTo(getIntegerValueForNullCheck(entry2.getTyre()));
                }
            }
        }

        return value;
    }
    private Integer getIntegerValueForNullCheck (Object o) {
        return o == null ? 0 : 1;
    }
}

including code that tests the sorting:

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.*;

public class Sorting {

    public static void main(String[] args) {

        List<Car> cars = new LinkedList<>();


        Date today = new Date();
        Instant now = Instant.now();
        Instant after = now.plus(Duration.ofDays(1));
        Date tomorrow = Date.from(after);

        cars.add(new Car(5, new Tyre(1,"1"), new Gear(1, "1"), today ));
        cars.add(new Car(5, new Tyre(1,"1"), null, today ));
        cars.add(new Car(5, null, null, today ));
        cars.add(new Car(4, null, null, today ));
        cars.add(new Car(3, null, null, tomorrow ));


        Collections.sort(cars, new CarComparator());
        System.out.println(cars);
    }

}

the output:

[Car{isReady=4, tyre=null, gear=null, deliveryDate=Thu Oct 10 11:27:20 IDT 2019}
, Car{isReady=5, tyre=null, gear=null, deliveryDate=Thu Oct 10 11:27:20 IDT 2019}
, Car{isReady=5, tyre=Tyre{id=1, grip='1'}, gear=null, deliveryDate=Thu Oct 10 11:27:20 IDT 2019}
, Car{isReady=5, tyre=Tyre{id=1, grip='1'}, gear=Gear{id=1, type='1'}, deliveryDate=Thu Oct 10 11:27:20 IDT 2019}
, Car{isReady=3, tyre=null, gear=null, deliveryDate=Fri Oct 11 11:27:20 IDT 2019}
]

8 Comments

@champ.exe what do you think?
@champ.exe you can change getIntegerValueForNullCheck if you want to make non null values appear before null values you can change the getIntegerValueForNullCheck as follows: private Integer getIntegerValueForNullCheck (Object o) { return o == null ? 1 : 0; }
@thomas even better than mine, didn't know that feature nice!
Suggestion on the style: nesting if(value == 0) { value = someComparison(); if(value == 0 ) { ... } } can lead to hard to read code, especially if there are more conditions that need to be taken into account. Using if(value == 0) { value = someComparison(); } if(value == 0) { value = someOtherComparison(); } ... removes that nesting and makes the code a lot more readable.
@Thomas let me start of by saying that your answer to this post solve the readability issue in the best way so kudos for that. regarding what you said to me, i am not 100% sure that i agree with you because separating the code into different if blocks suggests that each if block is independent from the other but in this example they depend on each and it seems to me that leaving the code as is describes the want behavior in the best way only if the deliveryDate is equal go ahead and check the Ready value and so on...
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.