Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Maximum of Stream with custom Comparator

Below is my code written specifically to use custom Comparator with max in Java 8 Stream.

import java.math.BigDecimal;
import java.util.*;

public class BigDecimalMax  {

    public static BigDecimal getBigDecimalMax(List<forTest> list) {

        return list.stream()
            .filter(t -> t.id % 2 == 0)
            .max(forTestComparator::compare)  //<-- syntax error ----------
            .orElse(null);
    }

    public static class forTestComparator implements Comparator<forTest> {

        @Override
        public int compare(forTest val1, forTest val2) {
            return val1.value.compareTo(val2.value);
        }
    }

    public static void main(String[] args) {

        List<forTest> lst = new ArrayList<>();
        Random rn = new Random();
        BigDecimalMax bdm = new BigDecimalMax();

        for (int i=1; i<22; ++i) {
            lst.add(bdm.new forTest(i, BigDecimal.valueOf(rn.nextLong())));
        }

        System.out.println(getBigDecimalMax(lst));

    }

    class forTest {
        public int id;
        public BigDecimal value;

        forTest(int id, BigDecimal value) {
            this.id = id;
            this.value = value;
        }

        @Override
        public String toString() {
            return "forTest{" +
                    "id=" + id +
                    ", value=" + value +
                    '}';
        }
    }
}

I'm getting a syntax error on a method reference which I don't understand.

Error:(15, 18) java: incompatible types: invalid method reference
    cannot find symbol
      symbol:   method compare(BigDecimalMax.forTest, BigDecimalMax.forTest)
      location: class BigDecimalMax.forTestComparator

while IntelliJ IDEA complains that Non-static method cannot be referenced from a static context.

What exactly am I doing wrong here?


ADDITIONAL EXPLANATION (04/24/14):

  1. I understand now the reason for the syntax error. Thank you.

  2. Was custom Comparator actually needed here?

Since BigDecimal implements Comparable but does not seem to implement Comparator ( it has CompareTo() but no Compare()) I thought that custom Comparator was necessary. That's why I could not just use Comparator.comparing(ft -> ft.value). Is there a flaw in my logic?

like image 759
PM 77-1 Avatar asked Nov 30 '22 00:11

PM 77-1


1 Answers

Sotirios Delimanolis' answer shows how to fix the problems, but I have a few things to add.

If you already have a class that implements Comparator, you don't need to use a method reference to its compare() method. You can just pass an instance of it directly, since max() takes a reference to a Comparator:

    .max(new forTestComparator())

or

    forTestComparator instance = new forTestComparator();
    ...
    .max(instance)

However, the combinator functions on Comparator usually make it unnecessary to have a class that implements Comparator. For example, you can get rid of the forTestComparator class entirely and just do this:

    .max(Comparator.comparing(ft -> ft.value))

or if forTest were to have the obvious getValue() method, one could rewrite the stream max() call as follows:

    .max(Comparator.comparing(forTest::getValue))

In addition, if you wanted to make forTest implement the Comparable interface, you could do this:

public class forTest implements Comparable<forTest> {
    @Override
    public int compareTo(forTest other) {
        return this.value.compareTo(other.value);
    }
    ...
}

and the way to use max() on a Comparable is:

    .max(Comparator.naturalOrder())

Two style notes:

  1. I strongly discourage using orElse(null) on instances of Optional. This is allowed, though probably its main purpose to retrofit use of new Java 8 APIs into code that's expecting null to indicate the absence of a value. Avoid orElse(null) if you possibly can, since this forces the caller to check for null. Instead, substitute an actual value to replace an absent value, or return the Optional itself to the caller, so the caller can apply whatever policy it wants.

  2. I recommend sticking to the established Java naming conventions of capitalized, mixed-case class names. The class names forTest and forTestComparator make this code kind of difficult to work with, since they don't look like class names.

like image 194
Stuart Marks Avatar answered Dec 20 '22 10:12

Stuart Marks