1

String class implements Comparable interface and we can see that the results are the lexicographic difference between two strings, which makes sense:

String s1 = "AB";
String s2 = "AF";
  
System.out.println(s1.compareTo(s2)); 

Will output -4, whereas if we switch the arguments outputs 4.

What I don't understand is the comparison with strings with different cases, i.e:

String s1 = "Ac";
String s2 = "AD";
  
System.out.println(s1.compareTo(s2)); 

This will output 31 it just gives me the difference between the two first different charaters in the string according to the ASCII table, it's telling me "Ac" is larger than "AD" by 31.

Now, I know this is easily fixable by having a custom comparator, anyway I fail to see how this result given by the default comparator could be of any use, what is the purpose of it?

When I compare strings I often just want to know if they are equal and less often if one is larger than the other regardless if they are upper or lower case, wouldn't compare case insensitive by default be better?

anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • 2
    The cases are not different: the `'B' - 'F'` is -4, `'c'-'D'` is 31, but this is just an implementation detail. The `compareTo()` method is only defined in terms of the sign of the result (less than zero, zero, greater than zero) – Thomas Kläger Jul 19 '21 at 09:12
  • Does this answer your question? [How do I make my string comparison case-insensitive?](https://stackoverflow.com/questions/2220400/how-do-i-make-my-string-comparison-case-insensitive) – Murat Karagöz Jul 19 '21 at 09:13
  • I am aware of how compareTo works and how to make it case insensitive, my question is how this result, being counterintuitive, is useful. – anastaciu Jul 19 '21 at 09:15
  • 2
    How would a more intuitive case-sensitive `compareTo` behave? – Sweeper Jul 19 '21 at 09:17
  • @Sweeper, wouldn't compare case insensitive by default be better? If I'm comparing two strings, I usually don't care if it's upper or lower, I just want to know wich is greater. – anastaciu Jul 19 '21 at 09:19
  • 2
    @anastaciu So would you think that `equals` should be case insensitive by default too? – Sweeper Jul 19 '21 at 09:20
  • @Sweeper I hand't thought about that, could be though it's the same principle. – anastaciu Jul 19 '21 at 09:24
  • 1
    Well given the complexities of case insensitivity, it is a good thing that the default for `compareTo` and `equals` is simple and case sensitive. See https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#toUpperCase() to get a feel of what I am talking about. Then take a look at it source code. – Stephen C Jul 19 '21 at 09:29
  • Anyhow, the spec is the spec, so you just have to live with it. – Stephen C Jul 19 '21 at 09:31
  • @StephenC, yes, for sure, I'm not a revolutionary, just a curious person ;) – anastaciu Jul 19 '21 at 09:33

2 Answers2

2

The default case sensitive comparator is useful if you precisely want just that behavior: A case sensitive comparison between two strings. In fact, Ac is lexicographically larger than AD. If you want a case insensitive comparison, then use String#compareToIgnoreCase, for example:

String s1 = "Ac";
String s2 = "AD";

System.out.println(s1.compareToIgnoreCase(s2));  // -1 => Ac < AD, ignoring case
Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
  • I see, still, it's a bit counterintuitive. Shouldn't it be the other way around, compare case insensitive by default? – anastaciu Jul 19 '21 at 09:13
  • 1
    @anastaciu The `Comparable` implementation is ideally "consistent with `equals`". And `"A"` is definitely not equal to `"a"`. It would be more counterintuitive for it to be case-insensitive. – Slaw Jul 19 '21 at 10:30
  • @Slaw that's a fair point. – anastaciu Jul 19 '21 at 11:20
1

The actual number returned by compareTo is not meant to be significant as far as Comparable's contract is concerned. It's not meant to be "useful" but for its sign.

The only thing that is relevant is how the value compares to zero (> 0, == 0, < 0). In other words, don't count on the actual number returned, it could change with implementation versions (without breaking the contract). And honestly, if I find myself using the value returned outside of the Comparable contract, I'd be sure I have a code smell.

If you want the same number always, you can use the sign of the result:

System.out.println(Math.signum(s1.compareTo(s2))) //cast if needed

Which will always produce -1.0, 0.0, or 1.0 depending on the result, and you can rely on it.

ernest_k
  • 44,416
  • 5
  • 53
  • 99
  • Yes that's right, we just use it to know if they are equal, and what's the lager one, though less often. This is why I think it would be useful to have compareTo be case insensitive. – anastaciu Jul 19 '21 at 09:25
  • I would like to thank you for your answer, both answers are good, I have to choose one so I opted fro the one with more votes, as I always do, sorry about that. – anastaciu Jul 19 '21 at 17:25
  • 1
    @anastaciu there's absolutely nothing to be sorry about. There can only be one accepted answer and I see that you have an objective way to pick :-) – ernest_k Jul 19 '21 at 17:43