Since this is a capturing lambda, indeed a new Predicate
instance will be returned all the time on each call to distinctByKey
; but this will happen per entire stream, not per each individual element.
If you are willing to run your example with:
Djdk.internal.lambda.dumpProxyClasses=/Your/Path/Here
you would see that a class
is generated for your the implementation of the Predicate
. Because this is a stateful lambda - it captures the CHM
and Function
, it will have a private
constructor and a static factory method
that returns an instance.
Each call of distinctByKey
will produce a different instance, but that instance will be reused for each element of the Stream. Things might be a bit more obvious if you run this example:
public static <T> Predicate<T> distinctByKey(Function<? super T,Object> keyExtractor) {
Map<Object,Boolean> seen = new ConcurrentHashMap<>();
Predicate<T> predicate = t -> {
Object obj = keyExtractor.apply(t);
System.out.println("stream element : " + obj);
return seen.putIfAbsent(obj, Boolean.TRUE) == null;
};
System.out.println("Predicate with hash :" + predicate.hashCode());
return predicate;
}
@Getter
@AllArgsConstructor
static class User {
private final String name;
}
public static void main(String[] args) {
Stream.of(new User("a"), new User("b"))
.filter(distinctByKey(User::getName))
.collect(Collectors.toList());
}
This will output:
Predicate with hash :1259475182
stream element : a
stream element : b
A single Predicate
for both elements of the Stream.
If you add another filter
:
Stream.of(new User("a"), new User("b"))
.filter(distinctByKey(User::getName))
.filter(distinctByKey(User::getName))
.collect(Collectors.toList());
There will be two Predicate
s:
Predicate with hash :1259475182
Predicate with hash :1072591677
stream element : a
stream element : a
stream element : b
stream element : b