Are multiple comparators being returned? - java

PriorityQueue<Student> pq = new PriorityQueue<(
Comparator.comparing(Student::getCgpa).reversed()
.thenComparing(Student::getFname)
.thenComparing(Student::getToken)
);
Are multiple comparators being returned? If so, how,
because this constructor only returns one.

"Are multiple comparators being returned?" - Yes, on a techical level. Each Comparator except for the last in the chain is used to create another Comparator (through the calls reversed() and thenComparing()). But only one is passed along as a parameter to the PriorityQueue:
Comparator.comparing(Student::getCgpa) // <- 1st comparator created & returned
.reversed() // <- 2nd comparator created & returned
.thenComparing(Student::getFname) // <- 3nd comparator created & returned
.thenComparing(Student::getToken) // <- 4th comparator created, returned & passed along as argument

Are multiple comparators being returned?
No
if so how because this constructoronly returns one?
From the thenComparing documentation, the given comparator is chained with the previous in one Comparator that can use several rules to sort values
From the code
default Comparator<T> thenComparing(Comparator<? super T> other) {
Objects.requireNonNull(other);
return (Comparator<T> & Serializable) (c1, c2) -> {
int res = compare(c1, c2);
return (res != 0) ? res : other.compare(c1, c2);
};
}
The new comparator apply this
use its own comparing test
if value is 0 (equals element) then the parameter one is used to return its decision
So in fact, using multiple chaining results in recursivse tests
Comparator<Foo> cmp = Comparator.comparingInt(Foo::getA)
.thenComparing(Foo::getB)
.thenComparing(Foo::getC)
.thenComparing(Foo::getD);
getA -> 1
-> -1
-> 0 -> getB -> 1
-> -1
-> 0 -> getC -> 1
-> -1
-> 0 -> getC -> 1
-> -1
...

Related

Java: using Collection.sort and lambda expression in combination with the ternary operator leads to unexpected result

I have the following class:
import java.util.*;
class ClassA {
public static void main(String...args) {
var list = new ArrayList<String>();
list.add("test");
list.add("abc");
Collections.sort(list, (a, b) -> a.length() >= b.length() ? 1 : 0); // [test, abc]
//Collections.sort(list, (a, b) -> a.compareTo(b)); // [abc, test]
System.out.println(list);
}
}
I thought Collection.sort() would sort accending. I thought a.length() is 4 and b.length() is 3 which would evaluate as (4 >= 3) true. So a.length() >= b.length() ? 1 : 0 would return 1 and mean that "test" is greater than "abc". So it should be sorted like [abc, test].
But I only get the result [abc, test] when I change the code to a.length() >= b.length() ? 1 : -1, which seems to indicate that a.length() >= b.length()evaluates as false. Why is that?
Why does the first lambda expression lead to [test, abc], while the modified version leads to [abc, test]?
Collections.sort(list, (a, b) -> a.compareTo(b)); works like expected.
Read the doc of Comparator: compare, or the lambda you're using to implement it, must return a negative value if a < b, not just a positive value if a > b. It must also return zero if a is equal to (or compares equal to) b. This differs from the expectation in some other programming languages, which is, I assume, what confused you.
It would be an entirely valid implementation of sort to never pay attention to positive outputs, only negative outputs. It would appear that it didn't do the comparison in the order you expected.
You can implement this comparator correctly as (a, b) -> Integer.compare(a.length(), b.length()), or much more simply as Comparator.comparingInt(String::length).

BinaryOpertor for List<Integer> to add the lists

In previous question I asked previously Which FunctionalInterface should I use?
Now I was trying to add to List<Integer> and not just two Integers a and b, such that each index adds to the same index of another list.
I had previously
BinaryOperator<Integer> binaryOperator = Integer::sum;
for adding two integers using binaryOperator.apply(int a,int b). Is there a similar way like
BinaryOperator<List<Integer>> binaryOperator = List<Integer>::sum;
and then get the result in List<Integer> cList?
if you want to perform some computation on the elements at corresponding indices (in this specific case the summation) there's no need to use a BinaryOperator, instead use IntStream.range to generate the indices:
// generates numbers from 0 until list.size exclusive
IntStream.range(0, list.size())....
// generates numbers from 0 until the minimum of the two lists exclusive if needed
IntStream.range(0, Math.min(list.size(), list2.size()))....
The common name for this type of logic is "zip"; i.e. when given two input sequences it produces an output sequence in which every two elements from the input sequences at the same position are combined using some function.
There's no built-in method in the standard library for this, but you can find some generic implementations here.
for example, using the zip method in the accepted answer of the linked post, you could simply do:
List<Integer> result = zip(f.stream(), s.stream(), (l, r) -> l + r).collect(toList());
or using method reference:
List<Integer> result = zip(f.stream(), s.stream(), Math::addExact).collect(toList());
where f and s are your list of integers.
You can use IntStream.range() to iterate over elements, then mapToObj() to map them to their sum and the collect() them in the third list.
Given that your lists are of same size
List<Integer> first = List.of(); // initialised
List<Integer> second = List.of(); // initialised
you can get the third list as :
List<Integer> third = IntStream.range(0, first.size())
.mapToObj(i -> first.get(i) + second.get(i))
.collect(Collectors.toList());
In terms of BinaryOperator, you can represent it as :
BinaryOperator<List<Integer>> listBinaryOperator = (a, b) -> IntStream.range(0, first.size())
.mapToObj(i -> first.get(i) + second.get(i))
// OR from your existing code
// .mapToObj(i -> binaryOperator.apply(first.get(i), second.get(i)))
.collect(Collectors.toList());
Or you can make it more readable by abstracting out the logic into a method and using it as :
BinaryOperator<List<Integer>> listBinaryOperator = YourClass::sumOfList;
where sumOfList is defined as :
private List<Integer> sumOfList(List<Integer> first, List<Integer> second) {
return IntStream.range(0, first.size())
.mapToObj(i -> first.get(i) + second.get(i))
.collect(Collectors.toList());
}
What you can do is define your own utility method whose single task is zipping the two input lists :
<T> List<T> zip(final List<? extends T> first, final List<? extends T> second, final BinaryOperator<T> operation)
{
return IntStream.range(0, Math.min(first.size(), second.size()))
.mapToObj(index -> operation.apply(first.get(index), second.get(index)))
.collect(Collectors.toList());
}
This way, you can sum your two input lists as :
zip(first, second, Integer::sum)

Stream filter by two parameters

I have googled quite a bit, but didn't found an answer.
Here is what I have:
parentList.forEach(p -> {
childList
.stream()
.filter(c -> p.id() == c.parentId())
.<...continue working on stream...>
});
I cannot find a way how to replace "filter" part with a Predicate as it seems that I need to pass argument to Predicate?
Your problem is that you're using a different Predicate each time, because although c is the parameter to your predicate, p also varies:
final Node p;
Predicate<Node> matchesParentId = c -> p.id() == c.id();
The reason your existing code compiles OK is that p is effectively final in the scope of the forEach block, so it can be used as a final field in a Predicate within that scope, with a lifetime of one forEach iteration.
You could do:
parentList.forEach(p -> {
childList
.stream()
.filter(matchesId(p))
.<...continue working on stream...>
});
private Predicate<Node> matchesId(Node other) {
return node -> node.id() == other.id();
}
But you won't be able to create one Predicate and reuse it as p varies.
You could write a BiPredicate and curry it into a Predicate. Unfortunately Java doesn't provide a curry method, so you have to provide your own.
private <T,U> Predicate<U> curry(BiPredicate<T,U> biPredicate, T t) {
return u -> biPredicate.test(t, u);
}
BiPredicate<Node,Node> nodesMatch = (a,b) -> a.id() == b.id();
parentList.forEach(p -> {
childList
.stream()
.filter(curry(nodesMatch, p))
.<...continue working on stream...>
});
This doesn't buy you all that much over and above the previous solution, but it's a bit more FP-nerdy. You're still creating a new Predicate for every p. Of course you could inline it rather than use the curry() method.
.filter(c -> nodesMatch.test(p, c))
It does mean you could have a selection of BiPredicate<Node,Node>s to plug in dynamically. If your BiPredicate were expensive to initialise, the many Predicates wrapped around it by currying would be cheap.
Or, you could map p and c into a single object, which allows you to submit the whole thing to a predicate:
Predicate<Pair<Node,Node>> nodesMatch = pair ->
pair.left().id() == pair.right().id();
parentList.forEach(p -> {
childList
.stream()
.map(c -> new Pair<Node>( c, p))
.filter(nodesMatch)
.map( pair -> pair.left() )
.<...continue working on stream...>
});
(Pair here is hypothetical, but a number of 3rd party libraries (e.g. Guava) provide one, or roll your own, or use new Node[] { c, p })

Java 8 predicate to select the max

This is a two-part question. I'm just learning about predicates in Java 8 and I need to know if there is a predicate that I can pass to the filter() method to select the max in a list of integers, as below:
List<Integer> numbers = ...
Integer max = numbers.stream()
.filter(n -> ???)
.getFirst()
.get();
The bigger question is: can predicates be chained mathematically so that one doesn't have to chain filter() or reduce() methods, such that the work done in one predicate can be reflected in or passed to the following one, so instead of writing:
list.stream()
.filter(/* predicate1 */
item -> {
// do some work and save in some local variable "var"
})
.filter(/* predicate2 */ var -> ...)
.reduce(predicate3)
.getFirst()
.get();
I could write:
list.stream()
.filter(predicate1 "andThen" predicate2 "andThen" reduce by predicate3)
.getFirst()
.get();
I speculate that there might be a way using the andThen() functionality or writing a custom predicate. Can anyone help me understand this better?
No, you cannot use filter(Predicate) to select the maximum value of a Stream.
You have multiple other options:
stream.max(Comparator.naturalOrder());
stream.sorted(Comparator.reverseOrder()).findFirst();
stream.mapToInt(i -> i).max();
stream.reduce(Integer::max);
Yes, Predicates can be chained by and(Predicate). The implementation basically looks like this:
Predicate<T> and(Predicate<? super T> other) {
return t -> test(t) && other.test(t);
}
You could also simply write your own:
static <T> Predicate<T> and(Predicate<? super T>... ps) {
return t -> Stream.of(ps).allMatch(p -> p.test(t));
}
Usage:
stream.filter(and(i -> i > 11, i -> i < 50))
You can apply multiple filters, so you usually chain the calls:
stream.filter(i -> i > 11).filter(i -> i < 50)
Do you understand what reduce(BinaryOperator) does? You cannot put a Predicate in there.
I have already used it above as an option to get the maximum value.

Java8 filter collect both type of value

Is there a way to collect both filtered and not filtered value in java 8 filter ?
One way is:
.filter( foo -> {
if(!foo.apply()){
// add to required collection
}
return foo.apply();
}
Is there a better alternative ?
Map<Boolean, List<Foo>> map =
collection.stream().collect(Collectors.partitioningBy(foo -> foo.isBar());
You can use a ternary operator with map, so that the function you apply is either the identity for some condition, In below example I calculating square of even numbers and keeping odd numbers as it is.
List<Integer> temp = arrays.stream()
.map(i -> i % 2 == 0 ? i*i : i)
.collect(Collectors.toList());
In your case it would be like this :
List<Integer> temp = arrays.stream()
.map(!foo.apply() -> ? doSomething: doSomethingElse)
.collect(Collectors.toList());

Categories