5

I have a requirement to sort objects(Zone) with their names(Zone Name : String) considering the case. I tried this with Java Comparator interface.

class NameComparator implements Comparator<Zone> {
    public int compare(Zone z1, Zone z2) {
        return z1.getZoneName().compareTo(z2.getZoneName());
   }
}

But this Comparator only sort lexicographically.

Ex : Required order for the zone names.

['Zone AAa3','Zone aaa3','Zone BBB7','Zone BBb7','Zone bbb7']

Current Output :

['Zone AAa3','Zone BBB7','Zone BBb7','Zone aaa3','Zone bbb7']

Is there any way to achieve this, other than writing a raw object sorting method?

Bilesh Ganguly
  • 3,792
  • 3
  • 36
  • 58

2 Answers2

10

There is a pre-defined comparator for case-insensitive sorting. You can use it with a Zone by extracting a sort key.

Comparator<Zone> byName = Comparator
    .comparing(Zone::getZoneName, String.CASE_INSENSITIVE_ORDER)
    .thenComparing(Zone::getZoneName);
erickson
  • 265,237
  • 58
  • 395
  • 493
  • This seems only work for Java 8.But looks like a easier way.Thank you. – shamika Dharmasiri Jun 06 '16 at 04:23
  • 2
    @shamikaDharmasiri Well, Java 7 ended support (including security) over a year ago, and Java 9 is almost here. Getting current is worth some effort. – erickson Jun 06 '16 at 04:29
  • But this does not solve the use case of ordering ('Zone AAa3' < 'Zone aaa3') - If input is Zone aaa3, Zone AAa3, then it does not return Zone AAa3, aaa3. – Subhrajyoti Majumder Jun 06 '16 at 06:10
  • @SubhrajyotiMajumder Updated to handle that case. – erickson Jun 06 '16 at 06:58
  • Whenever you find yourself writing `::compareTo`, you should rethink what you are doing. The presence of a `compareTo` method suggests the presence of a *natural order* which allows either, the use of `Comparator.naturalOrder()` or, in most cases, omitting the comparator entirely, i.e. `Comparator byName = Comparator.comparing(Zone::getZoneName, String.CASE_INSENSITIVE_ORDER).thenComparing(Zone::getZoneName);`. Though in this specific case, you perhaps want to use `Comparator.comparing(Zone::getZoneName, Collator.getInstance())` instead… – Holger Jun 16 '16 at 13:55
  • Thank you. Expecting equivalents for all the `comparing()` versions, I looked for the key-extractor–only version of `thenComparing()`, but overlooked it somehow. Because these strings appear to be some sort of code, rather than natural language, I opted for a locale-insensitive sort, but `Collator` might be the better choice depending on the application. – erickson Jun 16 '16 at 16:55
1

Tweak the logic, convert both name to lower case or upper case and then compare.

public int compare(Zone z1, Zone z2){
  if(z1.getZoneName().toLowerCase().equals(z2.getZoneName().toLowerCase()))
      return z1.getZoneName().compareTo(z2.getZoneName());
  else
      return z1.getZoneName().toLowerCase().compareTo(z2.getZoneName().toLowerCase());
}

Using lambda -

Comparator<Zone> byName = (z1, z2)->{
    if(z1.getZoneName().toLowerCase().equals(z2.getZoneName().toLowerCase()))
       return z1.getZoneName().compareTo(z2.getZoneName());
    else
       return z1.getZoneName().toLowerCase().compareTo(z2.getZoneName().toLowerCase());
};
Subhrajyoti Majumder
  • 40,646
  • 13
  • 77
  • 103
  • Then the case is not considered.I also need the case to be considered.I cannot assure this order, 'Zone AAa3' < 'Zone aaa3' – shamika Dharmasiri Jun 06 '16 at 03:57
  • updated answer - supported use case 'Zone AAa3' < 'Zone aaa3' – Subhrajyoti Majumder Jun 06 '16 at 04:01
  • The conversion to lower case is an unnecessary waste of performance and may not cover all cases of Unicode letters. Mind the existence of [`String.compareToIgnoreCase(String)`](https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#compareToIgnoreCase(java.lang.String)) and [`String.equalsIgnoreCase(String)`](https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#equalsIgnoreCase(java.lang.String)) which perform exactly the desired operation. – Holger Jun 16 '16 at 13:59