[Java] (Java SE8 Gold exam preparation) A memo for those who want to get along with Comparable and Comparator.

16 minute read

If anyone finds a mistake, please kindly point it out.

Introduction

I myself was very confused because I couldn’t organize my mind at all while studying for the Java Gold exam, so I organized it as a reflection. I feel like I’m getting along a little with this ٩ (‘ω’) و

What is it in the first place?

What are Comparable and Comparator? However, it is an interface used for mutual comparison of elements in both sorting and minimum / maximum value acquisition methods. Well, I understand that … I feel like.

Do you get the point when you hear “natural ordering”?

I wonder if it’s in ascending order. With that understanding, I wasn’t sure.
However, just by pinpointing what this is, the hurdle to understanding will be significantly lowered.
Therefore, please let me enter from the explanation here first.

Natural ordering is sorting according to Comparable

This “natural ordering” comes up frequently when browsing the Java API documentation.
However, as you can see by looking at it, “natural ordering” and “comparator” are already introduced like set products.

スクリーンショット 2020-07-20 20.57.47.png

  • Excerpt from Java API document (Collections class)

I’m sure there are some people who come up with a pin here, but natural ordering means sorting in the order implemented by Comparable.
Below is an excerpt of the Comparable Java API documentation.

public interface Comparable Forces global ordering on objects of each class that implements this interface. This ordering is called the natural ordering of the class, and the compareTo method of this class is called the natural comparison method. [Java API documentation-Comparable](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Comparable.html)

So what is Comparable?
As a position, it seems to be a common method of class (object).
Speaking of common methods, equals and toString may have come to mind.
However, unlike others, Comparable is not declared in Object. Therefore, it is necessary to implement it at the time of class creation if necessary.
Note that if you try to sort according to natural ordering without implementing Comparable, a “ClassCastException” will occur, so be careful if the target class implements Comparable.

By the way, it can be implemented in ascending or descending order, but there are some methods that sort by natural ordering that are clearly described as “ascending order” (sorts class sort, etc.), so natural ordering is probably based on ascending order implementation. I wonder if it is.

Then what about Comparator? ??

Comparator is an interface that came out after Comparable.
I presume that the main uses are probably as follows.

-I want to sort classes or fields for which Comparable is not implemented.
・ I want to do a little special sorting

And, perhaps to meet these expectations, Comparator is a “functional interface” that can be implemented flexibly. In addition, a static method that can simplify the implementation is also provided, and it seems that you may use the static method of Comparator to implement Comparable (laugh)

Also, if Comparator is null, it seems that the method applies Comparable or gives a “NullPointerException”.

so? Is it Comparable? Is it a Comparator?

If you think only as a test preparation, I think that there is no problem if you remember the extreme theory as follows. There were some methods that did not allow Comparable, but I wonder if it is OK to remember the basics as a set.

スクリーンショット 2020-07-20 21.29.00.png

On top of that, if you remember the difference in each implementation (difference in method name and number of arguments), Yoshi!

About the difference in implementation

The following is a summary of the differences between the implementations.
Keep in mind that the methods and the number of arguments to override are mandatory for the exam.

package interface Abstract method Number of arguments Return type Description
java.lang Comparable compareTo 1 int Compare itself with the argument object. Used for natural ordering
java.util Comparator compare 2 int Compare the arguments. Arbitrary ordering

I will introduce a concrete implementation example from the next section.

Implementation method

We will introduce each implementation example.
Also, if possible, we recommend that you check it on the actual machine.

↓ ↓ The sample to be introduced should move quickly with copy and paste below
Java cloud execution environment: https://paiza.io/

Comparable
As explained earlier, implement Comparable for the corresponding class.
The implementation example is as follows.

① Implemented in class


class Book implements Comparable<Book> {     //Implementation declaration.<>The inside is for comparison.

    public Integer id;
    public String title;
    
    Book(Integer id, String title){
        this.id = id;
        this.title = title;
    }

    @Override
    public int comareTo(Book book){          //Comparable abstract methods
        return this.id.compareTo(book.id);   //Compare arguments with yourself
    }
}

The compareTo method returns a negative integer if it is less than the specified object, zero if it is equal, and a positive integer if it is greater.
By the way (?), What is compareTo in the compareTo method? Am I the only one who thinks?

Now let’s sort using Comparable.

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //Creating a sort target
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(3, "Hamster breeding diary"));   
        bookList.add(new Book(2, "Howl's Moving Castle"));
        bookList.add(new Book(1, "Harry potter"));

        //Sort execution
        bookList.sort();  //Since no argument is specified, it is naturally ordered according to Comparable.

        //Display the sort target
        bookList.forEach( s -> System.out.println(s.id + ":" + s.title));
    }
}

<Execution result>
1:Harry potter
2:Howl's Moving Castle
3:Hamster breeding diary

Looking at this, I am vaguely wondering if it is an implementation because there are generics.

And you who became interested in Comparable.
Please know more (exactly) in the Java API docs ٩ (‘ω’) و ← thrown

Java API documentation-Comparable interface

Comparator
Now let’s move on to Comparator.
Since this is a “functional interface”, various implementation methods can be taken.
As far as I can tell, I will introduce each sample.

① Implemented in class

This is almost the same as Comparable.
I don’t think it makes much sense to use the Comparator in this way. Personally. ..

  • Since we want to assume that this Book class will be used in subsequent implementation methods, number the code related to which implementation method in the comments so that it can be linked.

class Book implements Comparator<Book> {         //① Implementation declaration.<>The inside is for comparison.

    public Integer id;
    public String title;
    
    Book(){}                                     //① Without this, you will get angry when you make a new

    Book(Integer id, String title){
        this.id = id;
        this.title = title;
    }

    public int getId(){                          // ②Comparator.Used as an argument for comparing
        return this.id;
    }

    public String getTitle(){                    // ②Comparator.used as an argument to thenComparing
        return this.title;
    }

    @Override 
    public int compare(Book book1, Book book2){   //① Abstract method of Comparator interface
        return book1.id - book2.id;              //Sort by id in ascending order
    }
}

The compare method compares the two arguments for ordering. Returns a negative integer if the first argument is less than the second argument, 0 if both are equal, and a positive integer if the first argument is greater than the second argument.

Let’s sort by the implemented Comparator.
I was shocked (?) At first glance, but in the case of implementation in a class, Comparator “news the class that implements Comparator” and passes it.
Ah … I’m going to get angry if I give a Comparator of a type different from the type to be sorted … Ah … I got angry (what)

import java.util.*;
public class Main {
    public static void main(String[] args) throws Exception {

        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(3, "Hamster breeding diary"));   
        bookList.add(new Book(2, "Howl's Moving Castle"));
        bookList.add(new Book(1, "Harry potter"));

        bookList.sort(new Book());  //← [Note] Be sure to remember because the way to pass the Comparator is special! Pass an instance of the class that contains the Comparator! !! !!
        bookList.forEach( s -> System.out.println(s.id + ":" + s.title));
    }
}

<Execution result>
1:Harry potter
2:Howl's Moving Castle
3:Hamster breeding diary

(2) Implemented by lambda expression (or anonymous class)

It is a writing style that can fully demonstrate that it is a functional interface.
What you can write in lambda expression can be written in anonymous class, so I will introduce both.

** Lambda expression implementation sample **

    Comparator<Book> comparator = 
        (b1, b2) -> b1.id.compareTo(b2.id);        //Ascending order of id

** Anonymous class implementation sample **

    Comparator<Book> comparator = new Comparator<Book>(){
        @Override
        public int compare(Book book1, Book book2) {
            return book1.id.compareTo(book2.id);   //Ascending order of id
        }
    };
  • It seems to be a little slower when implemented with Comparator than Comparable.
    At the time of implementation, it seems that you should also consider using Java’s static import mechanism.

Now let’s perform a sort.
This implements Comparator directly without making it variable.

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //Create a sort target
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(3, "Hamster breeding diary"));   
        bookList.add(new Book(2, "Howl's Moving Castle"));
        bookList.add(new Book(1, "Harry potter"));

        //Comparator(Anonymous class)Sort execution using
        bookList.sort(new Comparator<Book>(){
            @Override
            public int compare(Book book1, Book book2) {
                return book1.id.compareTo(book2.id);         //Ascending order of id
            }
        });

        //Display sort results
        System.out.println("Anonymous class(Ascending order of id)");
        bookList.forEach( s -> System.out.println(s.id + ":" + s.title));

        //Comparator(Lambda expression)Sort execution using * Arrange in descending order for easy understanding
        bookList.sort((b1, b2) -> b2.id.compareTo(b1.id));  //id descending

        //Display sort results
        System.out.println("Lambda expression (descending order of id)");
        bookList.forEach( s -> System.out.println(s.id + ":" + s.title));
    }
}

<Execution result>
Anonymous class(Ascending order of id)
1:Harry potter
2:Howl's Moving Castle
3:Hamster breeding diary
Lambda expression(id descending)
3:Hamster breeding diary
2:Howl's Moving Castle
1:Harry potter

(3) Implemented by the static method of Comparator

Comparator provides convenient static methods.
You can also prioritize the members to be sorted by combining this static method.
For simple sorting, I think it’s easiest to use this guy. (what

The sample using Stream API is as follows.

import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //Create a sort target
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(2, "Hamster breeding diary"));   
        bookList.add(new Book(2, "Howl's Moving Castle"));
        bookList.add(new Book(1, "Harry potter"));

        //Stream
        Stream<Book> streamList = bookList.stream();
        
        //Comparator.Sort and display using comparing
        streamList.sorted(Comparator.comparing(Book::getId)  //Sort ids first
                .thenComparing(Book::getTitle))              //Sort titles with second priority
                .forEach(s -> System.out.println(s.id + ":" + s.title));
    }
}

<Execution result>
1:Harry potter
2:Howl's Moving Castle
2:Hamster breeding diary

Compare () and thenComparing () are used in a chain.
Of course, you can use comparing () alone, or you can chain thenComparing () further.
Also, while saying that the correction is static, thenComparing () is a defalt method ^^;
In addition, Comparator provides the following methods.

# Method name Description
comparing Takes a function that extracts the sort key and returns a comparator that compares with that sort key
thenComparing Returns the lexicographic order comparator with the other comparator
naturalOrder Returns a comparator that compares Comparable objects in natural order (defalt method)
nullsFirst Returns a null-friendly comparator that considers null to be less than non-null
nullsFirst Returns a null-friendly comparator that considers null to be greater than non-null
reversed I will return a comparator that requires the reverse order of this comparator(default method)
reverseOrder Returns a comparator that mandates the reverse of natural ordering

And if you are interested, please refer to the Java API documentation.
My explanation is rough (laughs)
Java API documentation-Comparator interface

④ Actually, you don’t have to be a Comparator

So far, we have seen examples using Comparator, but if it is made the same as Comparator, it seems that it can actually be used with other methods without problems.
I felt like something was wrong, but it’s probably the same as using the static method of Comparator.

Let’s actually see an example.
First, create a class that will replace Compartor.

class LengthChecker {
    public static int check(Book b1, Book b2){         //Can be a static method unlike the compare method that is overridden when implementing Comparator
        return b1.title.length() - b2.title.length();  //title Ascending order of number of characters
    }
}
  • It may be cool if you can write using generics lightly. However, it will be difficult to understand, so I will use a direct expression.

After creating it, pass it as an argument to the method instead of Comparator.

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //Create a sort target
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(2, "Hamster breeding diary"));   
        bookList.add(new Book(2, "Howl's Moving Castle"));
        bookList.add(new Book(1, "Harry potter"));

        //Perform a sort
        bookList.sort(LengthChecker::check);  //← Unlike Comparator, it is necessary to specify the method

        //Display sort results
        bookList.forEach(s -> System.out.println(s.id + ":" + s.title));
    }
}

<Execution result>
2:Howl's Moving Castle
1:Harry potter
2:Hamster breeding diary

It’s working well … eh x ah sdf …
Basically, the place where Comparator is specified in the argument seems to be replaced.
I haven’t researched the specifications of this area, but it was a problem, so it may be better to remember it as much as “he”.
However, it seems that there are restrictions. As a result of various trials, it probably looks like this.

Introduction of main usage classes and interfaces

Introducing methods that use Comparable and Comparator to sort and get large and small elements. It’s super easy to write, so please refer to the Java API documentation for details.
Rather, it’s a good idea to familiarize yourself with the Java API documentation during this time! !! !!

Collection / Map interface

It is very often used in classes that implement this interface and in default / static methods.
In addition, please read on the assumption that the Book class mentioned in the previous section is implemented.

1List interface (default method)

A sorting method is provided as the default method.

# Method name Comparable Comparator Description
sort - I’ll sort

** Implementation sample **

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //Create a sort target
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(3, "Hamster breeding diary"));   
        bookList.add(new Book(2, "Howl's Moving Castle"));
        bookList.add(new Book(1, "Harry potter"));
        
        //Sort to Comparator
        bookList.sort(new Book());

    }
}

Click here for details on the List interface (https://docs.oracle.com/javase/jp/8/docs/api/java/util/List.html)

② Collections class (static method)

The implementation class of the Collections interface defines all static methods.

# Method name Comparable Comparator Description
sort I’ll sort
reverseOrder Specified in the argument of sort. I’ll sort in reverse order
reverse - 逆順にソ-トするよ
min I will return the smallest element of the sorted result
max I will return the maximum element of the sorted result

** Implementation sample **

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //Create a sort target
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(3, "Hamster breeding diary"));   
        bookList.add(new Book(2, "Howl's Moving Castle"));
        bookList.add(new Book(1, "Harry potter"));
    
        //Sort to Comparable
        Collections.sort(bookList);
        Collections.sort(bookList, Collections.reverseOrder());
        Collections.reverse(bookList);
        Collections.min(bookList);
        Collections.max(bookList);
        
        //Sort to Comparator
        Collections.sort(bookList, new Book());
        Collections.sort(bookList, Collections.reverseOrder(new Book()));
        Collections.min(bookList, new Book());
        Collections.max(bookList, new Book());
    }
}

Click here for details on the Collections class [https://docs.oracle.com/javase/jp/8/docs/api/java/util/Collections.html)

③ Arrays class

Various methods are provided for manipulating arrays.
All the methods introduced are static methods.

# Method name Comparable Comparator Description
sort I’ll sort
parallelSort Do you want to sort?
binarySearch After sorting, do you search?

** Implementation sample **
There are a lot of different types for each type, but it’s a good idea.
Please refer to the Java API documentation for details.

sample.java


import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //Create an array to sort
        String[] array = {"Ham assistant","Ham child","Hamjiro"};
        
        //Sort to Comparable
        Arrays.sort(array);
        Arrays.parallelSort(array);
        Arrays.binarySearch(array, "Ham assistant");  //Comes out first after sorting in natural order"Ham assistant"Will tell you the index number of
        
        //Create Comparator
        Comparator<String> comparator = new Comparator<String>(){
            @Override
            public int compare(String s1, String s2) {
                return s1.length() - s2.length();
            }
        };
        
        //Sort to Comparator
        Arrays.sort(array, comparator);
        Arrays.parallelSort(array, comparator);
        Arrays.binarySearch(array, "Ham assistant", comparator);  //Comes out first after sorting by comparator"Ham assistant"Will tell you the index number of
    }
}

Click here for details on the Arrays class [https://docs.oracle.com/javase/jp/8/docs/api/java/util/Arrays.html#sort-T:A-int-int-java.util .Comparator-)

④ TreeSet / TreeMap class

Since it is a collection that supports sorting by default, sorting is performed when the constructor is called. After that, Comparable and Comparator will be activated every time an element is added.

【point】
If you’re based on natural ordering, you won’t feel the shadow of Comparable at all, but be aware that you’ll get an exception if Comparable isn’t implemented in your class! !! !!

** Implementation sample **
Since it will be similar, only TreeSet is sampled.

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        
        //Create Comparator
        Comparator<Book> comparator = new Comparator<Book>(){
            @Override
            public int compare(Book b1, Book b2) {
                return b1.getTitle().length() - b2.getTitle().length();  //Longest number of title characters
            }
        };

        //Created in Comparable
        TreeSet<Book> book = new TreeSet<>();
        book.add(new Book(2, "Totoko Hamjiro"));
        book.add(new Book(3, "The World Unknown To Matsuko"));
        book.add(new Book(1, "Ham Festival from Monday"));
        System.out.println("<Comparable>");
        book.forEach( s -> System.out.println(s.getId() + ":" + s.getTitle()));
        
        //Created in Comparator
        TreeSet<Book> book2 = new TreeSet<>(comparator);
        book2.add(new Book(2, "Totoko Hamjiro"));
        book2.add(new Book(3, "The World Unknown To Matsuko"));
        book2.add(new Book(1, "Ham Festival from Monday"));
        System.out.println("<Comparator>");
        book2.forEach( s -> System.out.println(s.getId() + ":" + s.getTitle()));
    }
}
<Execution result>
<Comparable>
1:Ham Festival from Monday
2:Totoko Hamjiro
3:The World Unknown To Matsuko
<Comparator>
2:Totoko Hamjiro
1:Ham Festival from Monday
3:The World Unknown To Matsuko

Click here for details on TreeSet (https://docs.oracle.com/javase/jp/8/docs/api/java/util/Collections.html) / Click here for details on TreeMap (https://docs) .oracle.com/javase/jp/8/docs/api/java/util/TreeMap.html)

Stream API interface

It is possible to perform operations such as sorting on streamed objects.
You can also use the Collection and Map interface forces mentioned above.

# Method name Comparable Comparator Description
sort sort
min - ソ-トした結果の最小要素をOption型で返却
max - ソ-トした結果の最大要素をOption型で返却

** Implementation sample **

import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //Create a sort target
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(2, "Hamster breeding diary"));   
        bookList.add(new Book(2, "Howl's Moving Castle"));
        bookList.add(new Book(1, "Harry potter"));
        
        //Create Comparator
        Comparator<Book> comparator = new Comparator<Book>(){
            @Override
            public int compare(Book b1, Book b2) {
                return b1.getTitle().length() - b2.getTitle().length();  //Longest number of title characters
            }
        };

        //Sort to Comparable
        System.out.println("<Comparable:Shortest title>");
        bookList.stream().sorted().forEach(s -> System.out.println(s.getId() + ":" + s.getTitle()));

        //Sort to Comparator
        System.out.println("<Comparator:Shortest title>");
        bookList.stream().sorted(comparator).forEach(s -> System.out.println(s.getId() + ":" + s.getTitle()));  //← here
        Book minBook = bookList.stream().min(comparator).get();         //← here
        System.out.println("<Book with the shortest title>");
        System.out.println(minBook.getId() + ":" + minBook.getTitle());
        Book maxBook = bookList.stream().max(comparator).get();         //← here
        System.out.println("<Book with the longest title>");
        System.out.println(maxBook.getId() + ":" + maxBook.getTitle()); 
    }
}
<Execution result>
Comparable:Shortest title>
2:Hamster breeding diary
2:Howl's Moving Castle
1:Harry potter
Comparator:Shortest title>
2:Howl's Moving Castle
1:Harry potter
2:Hamster breeding diary
<Book with the shortest title>
2:Howl's Moving Castle
<Book with the longest title>
2:Hamster breeding diary

Click here for details on Stream API (https://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Stream.html#sorted–)

Finally look back quiz

JavaSE8Gold I’ll give you a problem.
First, there is the following code.


  List<Book> bookList = new ArrayList<>();
  bookList.add(new Book(1, "Ham assistant"));
  bookList.add(new Book(2, "Hamtaro"));
  bookList.add(new Book(3, "Ham Jiro"));

  Collections.sort(bookList); //← Here.

What code should I implement in my Book class to run this program and sort it in ascending order of Book.id?


// ①
    public int compareTo(Book book){
        return this.id.compareTo(book.id);
    }

// ②
    public boolean compareTo(Book book){
        return this.id.compareTo(book.id);
    }

// ③
    public int compare(Book book1, Book book2){
        return book1.id - book2.id;
    }

// ④
    public boolean compare(Book book1, Book book2){
        return book1.id > book2.id;
    }




The correct answer is ①!
Since we didn’t pass a Comparator as an argument, it’s a sort by natural ordering (Comparable).
Comparable’s abstract method is compareTo.
Also, there is one argument and the return value is an int type.

Summary

After the test is over, you can look back slowly, but before the test, you are busy with Kuromoto laps and input, so you can not afford it ^^;
… I don’t feel like I got more points if I did it before the exam (TvT)

References

I referred to the Java SE8 API documentation

Comparable Interface
Comparator Interface
Collections class
List interface
TreeMap class
TreeSet class
Arrays class
Stream API class

Also, as I was studying for the exam, I also read purple and black books, so my knowledge of Sochira is also integrated.