1

So im trying to sort a list of Car objects based off of whether the price of the car is even or not. SortEven sorts so that odd float to top however i need evens to float to top. but to me, the logic of SortEven should be swapping so that evens float to top Can someone explain how SortEven is working?

package Consumables;

import java.util.Comparator;

public class Cars implements Comparable<Cars>{
private String registrationNumber;
private int price;
private int seats;
private int mpg;

public static final Comparator<Cars> BY_MPG = new ByMPG();
public static final Comparator<Cars> BY_SEATS = new BySeats();
public static final Comparator<Cars> SORT_EVEN = new SortEven();

public Cars(String regNumber) {
    this.setRegistrationNumber(regNumber);
}

private static class ByMPG implements Comparator<Cars> {
    public int compare(Cars t, Cars c) {
        return t.getMpg() - c.getMpg();
    }
}

private static class BySeats implements Comparator<Cars> {
    public int compare(Cars t, Cars c) {
        return t.getSeats() - c.getSeats();
    }
}

What im trying to do/ logic: t.getPrice() % 2 evaluate to 0 if even. so i check the first object. if true t keeps location, if false, c.getPrice() is check to see if it is odd because if it is even that will be checked on second pass. finally return 0.

private static class SortEven implements Comparator<Cars> {
    public int compare(Cars t, Cars c) {
        if ((t.getPrice() % 2) == 0)
            return 1;
        else if ((c.getPrice() % 2) != 0)
            return -1;
        return 0;
    }


}

public String getRegistrationNumber() {
    return registrationNumber;
}

public void setRegistrationNumber(String registrationNumber) {
    this.registrationNumber = registrationNumber;
}

public int getPrice() {
    return price;
}

public void setPrice(int price) {
    this.price = price;
}

public int getSeats() {
    return seats;
}

public void setSeats(int seats) {
    this.seats = seats;
}

public int getMpg() {
    return mpg;
}

public void setMpg(int mpg) {
    this.mpg = mpg;
}

@Override
public boolean equals(Object obj) {
    // TODO Auto-generated method stub
    if (obj != null && obj instanceof Cars) {
        String regNumber = ((Cars)obj).getRegistrationNumber();
        if (regNumber != null && regNumber.equals(this.registrationNumber))
            return true;
    }

    return false;

}

@Override
public int hashCode() {
    return this.registrationNumber.hashCode();
}

@Override
public int compareTo(Cars o) {
    // TODO Auto-generated method stub
    if (o != null && o instanceof Cars) {
        if (this.getPrice() > o.getPrice())
            return 1;
        else if (this.getPrice() < o.getPrice())
            return -1;
    }
    return 0;
}

@Override
public String toString() {
    return "Registration Number: " + this.getRegistrationNumber()
            + "\n Price: " + this.getPrice()
            + "\n # of Seats : " + this.getSeats()
            + "\n MPG : " + this.getMpg();
}
}

Generating values randomly

import Consumables.Cars;

/**
 * @author briensmarandache
 *
 */
public class ComparatorSketchpad {

/**
 * @param args
 */
public static void main(String[] args) {
    // TODO Auto-generated method stub
    ArrayList<Cars> listOfCars = new ArrayList<>();

    for (int i=0; i < 100; i++) {
        Cars car = new Cars("H0-" + i);
        car.setMpg((int)(Math.random() * ((40 - 20) + 1)) + 20);
        car.setSeats((int)(Math.random() * ((8 - 2) + 1)) + 2);
        car.setPrice((int)(Math.random() * ((80_000 - 5_000) + 1)) + 5_000);
        listOfCars.add(car);
    }
    Collections.sort(listOfCars, Cars.SORT_EVEN);

    System.out.println("============ Print sorted by even price list ==========");

    Iterator<Cars> carIteratorByEven = listOfCars.iterator();
    while (carIteratorByEven.hasNext()) {
        System.out.println(carIteratorByEven.next());
    }

  }

}

This solves the issue, but im having trouble understanding why it works. it seems is it is operating backwards

private static class SortEven implements Comparator<Cars> {
    public int compare(Cars t, Cars c) {
        if ((t.getPrice() % 2) == 0 && (c.getPrice() % 2) == 0)
            return 0;
        else if ((t.getPrice() % 2) == 0 || (c.getPrice() % 2) != 0)
            return -1; // walk through debugger
        return 1; 
    }

Making suggested changes (ie. using && and complete case comparisons) i have.....

private static class SortEven implements Comparator<Cars> {
public int compare(Cars t, Cars c) {
    if (((t.getPrice() % 2) == 0 && (c.getPrice() % 2) == 0) || (t.getPrice() % 2) != 0 && (c.getPrice() % 2) != 0 )
        return 0; // no swap
    else if ((t.getPrice() % 2) == 0 && (c.getPrice() % 2) != 0)
        return 1; // no swap // walk through debugger 
    return -1; // swap
}

However this sort so that odd float to the top, opposite to what needs to happen

  • 3
    Possible duplicate of ["Comparison method violates its general contract!"](https://stackoverflow.com/questions/8327514/comparison-method-violates-its-general-contract) – Jacob G. Oct 08 '18 at 01:41
  • 1
    There are 4 possible results if you want to compare based on even: even-even, even-odd, odd-even, odd-odd. Even-odd and odd-even must be opposite. Not sure what you are doing in your implementation though. Give us a list of sample prices and tell us what the expected order of the list is. – Jai Oct 08 '18 at 01:43
  • 'ArrayList listOfCars = new ArrayList<>(); for (int i=0; i < 100; i++) { Cars car = new Cars("H0-" + i); car.setMpg((int)(Math.random() * ((40 - 20) + 1)) + 20); car.setSeats((int)(Math.random() * ((8 - 2) + 1)) + 2); car.setPrice((int)(Math.random() * ((80_000 - 5_000) + 1)) + 5_000); listOfCars.add(car); }` – Brien Smarandache Oct 08 '18 at 03:48
  • updated with implementation and a solution that i dont quite understand – Brien Smarandache Oct 08 '18 at 04:00
  • you're using the || operator on the second IF, but I believe you want an && there instead. Otherwise, when t.getPrice() is even, it will return -1 regardless of the value of c.getPrice (since the || operator will be true if any of the operands is true). – cleberz Oct 08 '18 at 04:04
  • so I switched it to the && operator, however the logic still doesnt make sense to me. when compare returns -1, it causes a swap of current and next element....right? to sort it so evens float to top, given cases provided by @Jai " 4 possible results if you want to compare based on even: even-even, even-odd, odd-even, odd-odd." then wouldn't i want to swap every time it is odd-even? but above "even when changed to &&" the swap happens when it is even-odd? am i misunderstanding something or having a major dyslexic spell? – Brien Smarandache Oct 08 '18 at 11:46
  • @cleberz is this logic correct for sort so evens float to top even-even no swap return 0, even-odd no swap return 1, odd-even swap return -1, odd-odd no swap return 0 – Brien Smarandache Oct 08 '18 at 14:21
  • @JacobG. could you take another look at this? i got it to work but dont understand why it works – Brien Smarandache Oct 08 '18 at 15:30
  • 0, 1 and -1 don't mean "swap/no-swap". Please take a look at the documentation https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html#compare-T-T- `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.` Check you code again and see if this is really what you're needing in order to put evens at the top, and odd to the bottom (I prefer saying 'left' and 'right', though). – cleberz Oct 08 '18 at 19:11
  • @cleberz I see, i thought the 0,1,-1 where just signal to swap. – Brien Smarandache Oct 08 '18 at 19:50

2 Answers2

0

From the Comparator documentation,

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.

Basically, -1 and 1 don't necessarily mean swap/no-swap, they just mean smaller than or greater then. The sorter will decide whether to swap or not. (question originally answered in the comments section).

cleberz
  • 601
  • 7
  • 11
0

So i refactored some of my original solution and found a logic flaw that was probably caused by the excessive use of chained conditional statements. Using XOR in my conditional statement made things a-lot cleaner and easier to read. should have done a truth table from the start.

/*
 * SortEven Logic custom sort method
 *      GIVEN:
 *  +   odd ---> should shift right (heavy weight)
 *  +   evens -> should shift left (light weight)
 *  +   n modulo 2 of any number n should equate to zero 
 *              as 2 divides n perfectly (w/o remainder) 
 *      
 *      Cases: even-even, even-odd, odd-even, odd-odd
 *          odd-odd and even-even are equal ---> in that they are 
 *          comparing 2 items that "weigh" the same. Thus, operations
 *          should be equal to each other. In context of sorting based off of whether 
 *          even is t or c, the FIRST condition test whether even-even or odd-odd is present
 *          and return zero if true. *** REFER TO TRUTH TABLE IF FIRST CONDITION IS CONFUSING ***
 *          SECOND condition, we only need to check if (t)arget element is odd. *************
 *          *********************************************************************************
 *                  MAY NEED TO CHANGE equals() TO MAINTAIN INTGRETY OF compareTo()
 *          *********************************************************************************
 *          THIRD condition, (t)arget is even 
 * 
 * 
 *      Truth Table  (even/odd = T/F)  (t/c = A/B) (t = (t.getPrice() % 2 == 0))  (c = (c.getPrice() % 2 == 0))
 * 
 *          A | B |     (A AND B) OR (!A AND !B)    |   !(A XOR B)
 *          T | T |             T                   |       T
 *          T | F |             F                   |       F
 *          F | T |             F                   |       F
 *          F | F |             T                   |       T   
 *      
 * 
 */
private static class SortEven implements Comparator<Cars> {
    public int compare(Cars t, Cars c) {
        if ( !( ((t.getPrice() % 2) == 0) ^ ((c.getPrice() % 2) == 0)) ) // check if even-even or odd-odd
            return 0;
        else if ((t.getPrice() % 2) != 0) // check if first element is odd; if so
            return 1; // heavy weight found
        return -1; // light weight found
    }

}