29 October 2021

Creating Comparator Objects

Functional Interfaces A functional interface is an interface that requires exactly one method.

Lambdas

picture of lambs

These are anonymous functions. They have various guises.

Quickies Here is a lambda that squares. In this form the right-hand side is evaluated and it is returned.


x -> x*x

Here is a Quickie that adds.


(x, y) -> x + y

Note the need for parentheses when there are two or more arguments.

This is an argless quickie lambda.


() -> System.out.println("foo");

What if you have several lines of code? Contain the code in curly braces. However, if you do this, you must have an explicit return statement.


(a, b) ->
{
    if(a.x == b.x)
    {
        return a.y - b.y;
    }
    return a.x - b.x;
}

Can I restrict the types that are passed in? Yes. Like so.


(OrderedPair a, OrderedPair b) ->
{
    if(a.x == b.x)
    {
        return a.y - b.y;
    }
    return a.x - b.x;
}

This lambda will only accept objects of OrderedPair type. This is possible in quickie lambdas too.


(double x) -> x*x

Implementing Comparable

Firstly, if you are going to create sortabe objects, it is a smart idea to have them implement the Comparable<T>, an interface specifies one method, compareTo.


public class OrderedPair implements Comparable<OrderedPair>
{
    private final int x;
    private final int y;
    public OrderedPair(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    public int compareTo(OrderedPair q)
    {
        if(x == q.x)
        {
            return y - q.y;
        }
        return x - q.x;
    }
    @Override 
    public String toString()
    {
        return String.format("(%s, %s)", x, y);
    }
    public static void main(String[] args)
    {
    }
}

The compareTo method works like this. If, metaphorically, x < y, then x.compareTo(y) < 0. This method will compare the ordered pairs in "dictionary" order. It will first compare the x-coördinates of the two ordered pairs; if they are equal, it will then compare the y-coördinates.

A big advantage to doing this lies in the static method sort in the class java.util.Collections. This method will use compareTo to sort a list of OrderedPairs. Here we make it happen.


import java.util.ArrayList;
import java.util.Collections;

public class OrderedPair implements Comparable<OrderedPair>
{
    private final int x;
    private final int y;
    public OrderedPair(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    public int compareTo(OrderedPair q)
    {
        if(x == q.x)
        {
            return y - q.y;
        }
        return x - q.x;
    }
    @Override 
    public String toString()
    {
        return String.format("(%s, %s)", x, y);
    }
    public static void main(String[] args)
    {
        ArrayList<OrderedPair> pairs = new ArrayList<>();
        pairs.add(new OrderedPair(1,2));
        pairs.add(new OrderedPair(2,1));
        pairs.add(new OrderedPair(4,5));
        pairs.add(new OrderedPair(5,3));
        pairs.add(new OrderedPair(3,4));
        pairs.add(new OrderedPair(3,5));
        pairs.add(new OrderedPair(3,0));
        pairs.add(new OrderedPair(3,6));
        pairs.add(new OrderedPair(1, -3));
        pairs.add(new OrderedPair(6, 2));
        pairs.add(new OrderedPair(7, -1));
        System.out.println(pairs);
        Collections.sort(pairs);
        System.out.println(pairs);
    }
}

Compile and run.

$ javac OrderedPair.java 
$ java OrderedPair
[(1, 2), (2, 1), (4, 5), (5, 3), (3, 4), (3, 5), 
     (3, 0), (3, 6), (1, -3), (6, 2), (7, -1)]
[(1, -3), (1, 2), (2, 1), (3, 0), (3, 4), (3, 5), 
     (3, 6), (4, 5), (5, 3), (6, 2), (7, -1)]

The Collections.sort method sorts our pairs in dictionary order.

The compareTo method is refererred to in the documentation as the natural ordering.

Creating a Custom Sort with a Comparator

We are going to use lambdas to do this. Here is the code.


Comparator<OrderedPair> yFirst = (a, b) ->
{
    if(a.y == b.y)
    {
        return a.x - b.x;
    }
    return a.y - b.y;
};

What is happening here? Comparator<T> is an interface specifying one method, compare(T x, T y). This interface is functional.

So Java creates an object that is an instance of an nameless class that implements Comparator<T> and has the lambda as its method compare(T x, T y).

We will pass this as the second argument to Collections.sort() and the pairs will be sorted by their y-value first then their x-value. Note, in the code below, that you need an additional import.


import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class OrderedPair implements Comparable<OrderedPair>
{
    private final int x;
    private final int y;
    public OrderedPair(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    public int compareTo(OrderedPair q)
    {
        if(x == q.x)
        {
            return y - q.y;
        }
        return x - q.x;
    }
    @Override 
    public String toString()
    {
        return String.format("(%s, %s)", x, y);
    }
    public static void main(String[] args)
    {
        ArrayList<OrderedPair> pairs = new ArrayList<>();
        pairs.add(new OrderedPair(1,2));
        pairs.add(new OrderedPair(2,1));
        pairs.add(new OrderedPair(4,5));
        pairs.add(new OrderedPair(5,3));
        pairs.add(new OrderedPair(3,4));
        pairs.add(new OrderedPair(3,5));
        pairs.add(new OrderedPair(3,0));
        pairs.add(new OrderedPair(3,6));
        pairs.add(new OrderedPair(1, -3));
        pairs.add(new OrderedPair(6, 2));
        pairs.add(new OrderedPair(7, -1));
        System.out.println(pairs);
        Collections.sort(pairs);
        System.out.println(pairs);
        Comparator<OrderedPair> yFirst = (a, b) ->
        {
            if(a.y == b.y)
            {
                return a.x - b.x;
            }
            return a.y - b.y;
        };
        Collections.sort(pairs);
        System.out.println(pairs);
    }
}

Run and Compile.

$ !javac
javac OrderedPair.java
$ java OrderedPair
[(1, 2), (2, 1), (4, 5), (5, 3), (3, 4), (3, 5), (3, 0), 
    (3, 6), (1, -3), (6, 2), (7, -1)]
[(1, -3), (1, 2), (2, 1), (3, 0), (3, 4), (3, 5), (3, 6), 
    (4, 5), (5, 3), (6, 2), (7, -1)]
[(1, -3), (1, 2), (2, 1), (3, 0), (3, 4), (3, 5), (3, 6), 
    (4, 5), (5, 3), (6, 2), (7, -1)]

FileIO and Exceptions

Exception handling comic strip

The Throwable Subtree

  
            -------------------
            |    Object       |
            -------------------
                    |
                    |
            -------------------
            |    Throwable    |
            -------------------
              /               \
             /                 \
   --------------          ------------
   |   Error    |          |Exception |
   --------------          ------------
                                |
     Inevitable      --------------------      
     Death!          | RuntimeException |
                     --------------------
                        Largely programmer
                        goofs:
                        IllegalArgumentException
                        ArithmeticException
                        IndexOutOfBoundsException

It is optional to guard against runtime exceptions. All other exceptions must be handled by the try-catch-throw-finally mechanism.