sortingjava-8comparatorcomparablestring-length

compareTo() method on integer (Java 8, Comparable, Comparator, sorting, String)


We can apply comparable & comparator both on integers, as like below:

List<Integer> intList1 = Arrays.asList(1,9,4,8,2,3,7,4,5);
Optional<Integer> val1 = intList1.stream().sorted((a, b) -> a.compareTo(b)).skip(1).findFirst();
Optional<Integer> val2 = intList1.stream().sorted((a,b) -> a >= b ? 1 : -1).skip(1).findFirst();

Same I am trying to apply on integer getting from String.length(), like below:

List<String> strList = Arrays.asList("ab", "abc", "abcd");

Optional<String> strVal1 = strList.stream().sorted((a, b) -> a.length() >= b.length() ? 1 : -1 )
                .skip(1).findFirst();
System.out.println(strVal1.get());

Optional<String> strVal2 = strList.stream().sorted((a, b) -> a.length().compareTo(a.length()))
                .skip(1).findFirst();
System.out.println(strVal2.get());

Question: Showing error while applying compareTo() on a.length() which returns integer only for variable strVal2

What did you try:

Tried with comparator() and comparable() on custom objects also.

what were you expecting:

Explanation for such behaviour.


Solution

  • In the first code snippet, a and b are of the reference type java.lang.Integer. This is a class that declares a method called compareTo. Therefore, a.compareTo(b) is valid.

    In the second code snippet, a.length() returns a value of the primitive type int. This is a very different type from java.lang.Integer. You cannot call methods on an int, because it is not a reference type, like a class. For more differences between reference types and primitive types, see this post.

    From the section of the Java Language Specification that talks about method invocations,

    If the form is Primary . [TypeArguments] Identifier, then let T be the type of the Primary expression. The type to search is T if T is a class or interface type, or the upper bound of T if T is a type variable.

    [...]

    It is a compile-time error if T is not a reference type.

    In your case, Primary is the expression a.length(), whose type is the primitive type int.


    To compare primitive ints, you can use the static Integer.compare method.

    (a, b) -> Integer.compare(a.length(), b.length())
    

    Or, instead of a lambda, pass a Comparator created using Comparator.comparingInt:

    Comparator.comparingInt(String::length)
    

    Note that the code that passed (a,b) -> a >= b ? 1 : -1 and (a, b) -> a.length() >= b.length() ? 1 : -1 are incorrect. The general contract of Comparator requires compareTo(x, y) to have the opposite sign from compare(y, x), or both should return 0.