Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How Predicate maintain state in Java 8

I am looking at this code and trying to understand the following piece of code.

copied from Stuart Marks answer

public static <T> Predicate<T> distinctByKey(Function<? super T,Object> keyExtractor) {
    Map<Object,Boolean> seen = new ConcurrentHashMap<>();
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}

BigDecimal totalShare = orders.stream()
    .filter(distinctByKey(o -> o.getCompany().getId()))
    .map(Order::getShare)
    .reduce(BigDecimal.ZERO, BigDecimal::add);

My question here is everytime distinctByKey will be called and resulting new ConcurrentHashMap. how it is maintaining the state using new ConcurrentHashMap<>(); ?

like image 448
Santanu Sahoo Avatar asked Nov 20 '17 08:11

Santanu Sahoo


People also ask

What is the predicate in Java 8?

What is the Predicate in Java 8? In Java 8 Predicate interface is defined in the java.util.function package. It contains one abstract method that is called test () method. This interface provides functionality to test/evaluate any condition. This interface is used in a lambda expression and method reference to evaluate the condition.

What is test () method in Java 8 predicate interface?

In Java 8 Predicate interface is defined in the java.util.function package. It contains one abstract method that is called test () method. This interface provides functionality to test/evaluate any condition. This interface is used in a lambda expression and method reference to evaluate the condition. Where T, The type of input of Predicate.

Do I need a new class to create a predicate?

You don't need to create a new class to create a Predicate. This is because Java 8 added a lambda syntax, which you can think of as a shorthand for anonymous inner classes implementing an interface with only one method. Here is an example:

How to use predicate in stream in Java?

The filter method of Stream takes Predicate as an argument. 1. By use of Java Predicate, you can move the conditional code in one place. 2. It improves code maintenance because if you want to make any change in condition then you have to make it in a central place.


1 Answers

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 Predicates:

Predicate with hash :1259475182
Predicate with hash :1072591677
stream element : a
stream element : a
stream element : b
stream element : b
like image 182
Eugene Avatar answered Oct 06 '22 04:10

Eugene