简体   繁体   中英

Java comparator: Two ordering criteria

I have a simple class that contains a string (name) and an integer (age). The objects, that shall be stored in the collection, must not have double name values and shall be sorted according to descending age. The first code example removes all double names, but doesn't contain a second ordering criterion:

public int compare(Person p1, Person p2) {  
    int reVal = 1;

       if(p1.getName().compareTo(p2.getName()) != 0){
       reVal = 1;       
       }
       else {
       reVal = 0;       
       }                               
         return reVal;                  
    } 

The next example comparator shall order the rest set of the objects, that doesn't contain any double names:

public int compare(Person p1, Person p2) {  
    boolean ageGt = (p1.getAge() > p2.getAge());
    int reVal = 1;

       if(p1.getName().compareTo(p2.getName()) != 0){
       if(scoreGt)
            reVal = -1;
       else 
            reVal = 1;      
       }
       else {
       reVal = 0;       
       }                               
         return reVal;                  
    } 

The second comparator orders the objects according their age values correctly, but it allows double names, which I don't understand, because the outer if-statement already checked if the names of both objects are equal. Why does that happen?

You have a fundamental problem here: you want at the same time to test for unicity and to order entries. There is no builtin collection which will check at the same time that entries are equal and that their comparison is 0.

For instance, two Set implementations are HashSet and TreeSet :

  • HashSet uses Object 's .equals() / .hashCode() to test for equality;
  • TreeSet uses a Comparator (or the objects' Comparable capability if they implement it) to test for equality.

This is not quite the same thing. In fact, with one particular JDK class, that is, BigDecimal , this can get quite surprising:

final BigDecimal one = new BigDecimal("1");
final BigDecimal oneDotZero = new BigDecimal("1.0");

final Set<BigDecimal> hashSet = new HashSet<>();
// BigDecimal implements Comparable of itself, so we can use that
final Set<BigDecimal> treeSet = new TreeSet<>();

hashSet.add(one);
hashSet.add(oneDotZero);
// hashSet's size is 2: one.equals(oneDotZero) == false

treeSet.add(one);
treeSet.add(oneDotZero);
// treeSet's size is... 1! one.compareTo(oneDotZero) == 0

You cannot both have your cake and eat it. Here, you want to test unicity according to the name and comparison according to the age, you must use a Map .

As to obtain a sorted list of persons, you will have to do a copy of this map's .values() as a list and use Collections.sort() . If you use Guava, this latter part is as simple as Ordering.natural().sortedCopy(theMap.values()) , provided your values implement Comparable .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM