Andreas Yankopolus
Andreas Yankopolus

Reputation: 931

How can I pass a comparison function into a Java method?

I'm porting some code from Python to Java 8 and am stuck on a method where one of the parameters is a matrix and the others is a comparison function, which in Python is something like np.gt or np.lt.

I'm finding this difficult to implement in Java, as Java comparisons (Comparator) only operate on complex types, and autoboxing doesn't seem to work.

What I'd like to write is:

public static boolean[][] matrix_check(double[][] matrix, Comparator<double> comparator) {
    // ...
}

But Comparator<double> obviously doesn't work. How should I implement this functionality in Java?

Upvotes: 1

Views: 2635

Answers (3)

Andreas Yankopolus
Andreas Yankopolus

Reputation: 931

I ended up using DoubleBinaryOperator.

DoubleBinaryOperator lt = (x, y) -> { if (x > y) return 1; else return -1; };
DoubleBinaryOperator gt = (x, y) -> { if (x < y) return 1; else return -1; };

Other binary operations are easily added.

Method definition and comparator usage then looks like:

public static Boolean[][] matrix_check(double[][] matrix, DoubleBinaryOperator comparator) {
  if (comparator.applyAsDouble(arg1, arg2) > 0) {
    // Logic if true.
  } else {
    // Logic if false.
 }

Upvotes: 1

Nikolas
Nikolas

Reputation: 44496

Having the defined method with Comparator as one of the formal parameters means that you can pass a lambda expression in it of a functional interface corresponding to Comparator. Don't forget to wrap the Comparator around the boxed types, since you cannot use the primitive ones.

The crucial is Comparator::compare which returns int and therefore behaves like a BiFunction<T, T, Integer>, shall be shortened as a lambda expression (or a method reference in some cases):

double[][] doubleArray = ...
Comparator<Double> comparator = (left, right) -> (int)(left - right);  
boolean[][] booleanArray = matrix_check(doubleArray, comparator);

The lambda expression might be passed directly as well:

double[][] newDoubleArray = matrix_check(doubleArray, (int)(left - right));

In the example above, you have to mind the precision lost with casting to int. The doubles are not suitable for the lambda expression explanation. For integers or objects implementing Comparable the whole thing would be much easier:

Comparator<Integer>  c1 = (l, r) -> l- r;           // l - r results in int
Comparator<String>   c2 = (l, r) -> l.compareTo(r); // implements Comparable by default
Comparator<MyObject> c3 = (l, r) -> l.compareTo(r); // must implement Comparable 

Upvotes: 4

Lesiak
Lesiak

Reputation: 26094

Java generics don’t support primitive types. You need to pass Comparator<Double> instead. You can compare both double and Double values with such a comparator. Comparing double values will leverage auto-boxing.

Upvotes: 3

Related Questions