Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 comparator with if clause

I want to sort a list of objects that I get from database by date. The problem is that location of the date(table1,table2) depends on ObjectType. If it is 1, than date should be picked from first table, if it is 2, than from second. I started like this:

     objectList.stream().sorted((h1,h2)->
     if(h1.getObjectType().getId()==1){
     h1.getObject().getTable1().getTime()}//here is the problem
     );

But then I got confused, because after if clause I can't just add .compareTo. Is there any way to make Java 8 comparator using lambda expressions with if clause?

like image 218
Alyona Avatar asked Jan 08 '23 10:01

Alyona


1 Answers

There are a few things going on at once here.

The first is that if you want to use an if-else statement inside of a lambda, you need to use a statement lambda (the kind with braces) instead of an expression lambda (in which braces are omitted). With a statement lambda, you have to use a return statement explicitly, whereas in an expression lambda the return value is implicitly just the result of evaluating the expression.

You have some conditional logic for selecting the value to be compared. I'll assume that this logic needs to be applied independently to each of the two objects being compared, so that means the straightforward way to proceed will require you to write out the logic twice.

Finally, you need to write some logic for the actual comparison.

For this example I'll assume that the objects being sorted are of type Obj and that the values they're being sorted on are of type ObjDate which I'll further assume to be comparable to each other. (That is, ObjDate implements Comparable<ObjDate>.) I'll also assume that the object ids are either 1 or 2 so I won't handle the case of the value being something else.

Here's a fully-written-out comparator using a statement lambda:

    objectList.stream().sorted((h1, h2) -> {
        ObjDate h1date;
        ObjDate h2date;

        if (h1.getObjectType().getId() == 1) {
            h1date = h1.getObject().getTable1().getTime();
        } else {
            h1date = h1.getObject().getTable2().getTime();
        }

        if (h2.getObjectType().getId() == 1) {
            h2date = h2.getObject().getTable1().getTime();
        } else {
            h2date = h2.getObject().getTable2().getTime();
        }

        return h1date.compareTo(h2date);
    });

Ugh! Writing comparators is fun, isn't it? :-)

Basically the idea is to apply the get-from-table1-or-table2 logic to extract the right value for the first object, and then the same with the second object. Finally, return the result of comparing them.

This will work, but there's some obvious duplicated code here. The common code can be referred to as the key extractor because given an object to be compared, it extracts a key that's used as the basis for comparison. Here's a key extractor method that does this for a single object:

ObjDate getSortingDate(Obj obj) {
    if (obj.getObjectType().getId() == 1) {
        return obj.getObject().getTable1().getTime();
    } else {
        return obj.getObject().getTable2().getTime();
    }
}

Now that we have this, we can simplify the comparator considerably:

    objectList.stream().sorted((h1, h2) -> {
        ObjDate h1date = getSortingDate(h1);
        ObjDate h2date = getSortingDate(h2);
        return h1date.compareTo(h2date);
    });

If we collapse the local variables, we can convert this into an expression lambda:

    objectList.stream().sorted(
        (h1, h2) -> getSortingDate(h1).compareTo(getSortingDate(h2)));

Finally, the idea of extracting a key from two objects and comparing them is so common with comparators that there's a helper method that does this for you: Comparator.comparing(keyExtractor). Given two objects, this runs the key extractor on both objects and compares them. (More precisely, it returns a function that does this.) We can use this directly to simplify things even further:

    objectList.stream().sorted(Comparator.comparing(this::getSortingDate));
like image 169
Stuart Marks Avatar answered Jan 10 '23 22:01

Stuart Marks