Sorting

Natural order

Certain types of object have a natural order, such as Integers (and other numeric types) - two is higher than one, five is higher than two etcetera. Other things has a pseudo-natural order, a human would likley sort a series of Strings into alphabetical order (though there might be some confusion if some the Strings started with numbers, characters or a mix of upper and lowercase)

These types of object are said to be comparable (i.e. we can compare '12' with '16' and determine that '12' would come first in an ascending sequence, similarly comparing 'X' and 'Y', we would determine that X would come first). When Java sorts Strings and Characters, it goes sightly further than a human might, sorting based on the order the characters are defined in the Unicode standard)

In order to sort we need a collection of objects (such as an ArrayList, though other types of collections can be sorted), and the items in the collection must be 'comparable'. Strings and Integers, for example, are already comparable, and can be sorted as follows:

//assumes studentNames is an ArrayList<String>
Collections.sort(studentNames);

Arrays of primitive types can be sorted as follows:

//assumes intArray is an int[]
Arrays.sort(intArray);

Comparable

For something to be sorted, any two of that type of thing should be able to be compared to another of that type, and it be determined within the first thing should come before or after it, or indeed be equal. How this is determined is bespoke to the items being considered. In Java, this means that to be sorted using a pseudo-natural order the objects in question must 'implement Comparable'

'Comparable' is an interface. In programming an interface is like a class that defines methods, but does not implement them. In this case, the 'Comparable' interface defines a method called compareTo which is intended to return a positive, negative or 0 value relating to how the object itself compares to another object (typically of the same type). Consider the following class to represent a 'James Bond':

public class JamesBond { 
    private String name;
    String getName() {
        return name;
    }
    
    private int greatness; //how good a Bond they were
    
    //bondNumber indicates the order in which they starred in their first James Bond film
    JamesBond(int bondNumber) {
        switch (bondNumber) {
            case 1:
                name = "Connery";
                greatness = 5;
                break;
            case 2:
                name = "Lazenby";
                greatness = 1;
                break;
            case 3:
                name = "Moore";
                greatness = 4;
                break;
            case 4:
                name = "Dalton";
                greatness = 2;
                break;
            case 5:
                name = "Brosnan";
                greatness = 3;
                break;
            case 6:
                name = "Craig";
                greatness = 6;
                break;
            default:
                name = "Imposter";
                greatness = 0;
        }
    }
}
As it stands, we could create a series of JamesBond objects, but we could not sort them - if we tried to write the following code (in the main method for example) we would get an error in the IDE saying that 'sort in Collections cannot be applied to ArrayList<JamesBond>'
JamesBond georgeLazenby = new JamesBond(2);
JamesBond danielCraig = new JamesBond(6);
JamesBond pierceBrosnan = new JamesBond(5);

ArrayList<JamesBond> bonds = new ArrayList<>();
bonds.add(georgeLazenby);
bonds.add(danielCraig);
bonds.add(pierceBrosnan);
    
Collections.sort(bonds);

Implementing Comparable

To implement Comparable, we must modify the JamesBond class so that it knows it declares that it implements Comparable (where the comparable type is another JamesBond object). We would modify the class declaration line as follows:

public class JamesBond implements Comparable<JamesBond> {
Secondly, we must implement the compareTo method (which is specified in the Comparable interface) inside the JamesBond class, as follows:
@Override
public int compareTo(JamesBond otherBond) {
    //e.g. if thisBond is Brosnan, and otherBond is Craig, a negative value will be returned
    //signifying that Brosnan comes before Craig (in order of ascending greatness)
    //if a bond is being compared to himself, then 0 will be returned
    return this.greatness - otherBond.greatness;
}
Note how the methods is annotated as being an Override this means that it is replacing the (non existant) implementation of the code in the Comparable interface

The code in the main method show no longer show an error on attempting to sort the JamesBond ArrayList and if you were to iterate through them and print their names, the output would appears as follows:

Lazenby
Brosnan
Craig
Correctly indicating the order of these three James Bonds from worst to best!

Task

Using the PokeTutor class that you created in the MiniProject, implement comparable, so PokeTutors can be compared on combat points (CP). Create a simple command line application to test this.

Custom comparator

Sometimes we need to be able to sort on other items - or we may not have a natural ordering as Strings / Integers and arguably James Bond actors do. In order to sort collections in this instance we need to use a custom comparator. Assuming the Bond class had a yearOfBirth property, we could create a custom comparator for that as follows:

public class BondAgeComparator implements Comparator<JamesBond> {
    @Override
    public int compare(JamesBond bond1, JamesBond bond2) {
        return bond1.getYearOfBirth() - bond2.getYearOfBirth();
    }
}
When we come to sort the collection of Bonds, we simply pass the comparator to the Collections.sort() method as an additional parameter as follows:
BondAgeComparator comparator = new BondAgeComparator();
Collections.sort(bonds, comparator);

Task

Add a year of birth property to the JamesBond class and modify the constructor so the year of births are set as follows:

Ensure you can sort a collection including all the Bond actors by year of birth

Add a property to hold the actor's first name, and implement a custom comparator to enable sorting on this field. Test again with all Bonds.