How to log filtered values in Java Streams

前端 未结 3 488
生来不讨喜
生来不讨喜 2021-02-12 18:49

I have a requirement to log/sysout the filtered values in Java Streams. I am able to log/sysout the non-filtered value using peek() method

相关标签:
3条回答
  • 2021-02-12 19:21

    You could use

    Map<Boolean,List<Person>> map = persons.stream()
        .collect(Collectors.partitioningBy(p -> "John".equals(p.getName())));
    System.out.println("filtered: " + map.get(true));
    List<Person> result = map.get(false);
    

    or, if you prefer a single-statement form:

    List<Person> result = persons.stream()
        .collect(Collectors.collectingAndThen(
            Collectors.partitioningBy(p -> "John".equals(p.getName())),
            map -> {
                System.out.println("filtered: " + map.get(true));
                return map.get(false);
            }));
    
    0 讨论(0)
  • 2021-02-12 19:25

    As there's no way to run terminal actions on elements matching opposite filters on the same stream, the best option may be just to use a condition in a peek's consumer.

    That avoids traversing the stream/collection twice.

    List<Person> persons = Arrays.asList(new Person("John"), new Person("Paul"));
    
    //A consumer that only logs "John" persons
    Consumer<Person> logger = p -> {
        if ("John".equals(p.getName())) //condition in consumer
            System.out.println("> " + p.getName());
    };
    

    Then we can pass that consumer to peek, only filtering for the ultimate action after that:

    List<Person> nonJohns = persons.stream()
                      .peek(logger)
                      .filter(p -> !"John".equals(p.getName()))
                      .collect(Collectors.toList());
    
    0 讨论(0)
  • 2021-02-12 19:34

    If you want to integrate it with Stream API, there's not much you can do other than introducing the logging manually. The safest way would be to introduce the logging in the filter() method itself:

    List<Person> filtered = persons.stream()
          .filter(p -> {
              if (!"John".equals(p.getName())) {
                  return true;
              } else {
                  System.out.println(p.getName());
                  return false;
              }})
          .collect(Collectors.toList());
    

    Keep in mind that introduction of side-effects to Stream API is shady and you need to be aware of what you're doing.


    You could also construct a generic wrapper solution:

    private static <T> Predicate<T> andLogFilteredOutValues(Predicate<T> predicate) {
        return value -> {
            if (predicate.test(value)) {
                return true;
            } else {
                System.out.println(value);
                return false;
            }
        };
    }
    

    and then simply:

    List<Person> persons = Arrays.asList(new Person("John"), new Person("Paul"));
    
    List<Person> filtered = persons.stream()
      .filter(andLogFilteredOutValues(p -> !"John".equals(p.getName())))
      .collect(Collectors.toList());
    

    ...or even make the action customizable:

    private static <T> Predicate<T> andLogFilteredOutValues(Predicate<T> predicate, Consumer<T> action) {
        Objects.requireNonNull(predicate);
        Objects.requireNonNull(action);
    
        return value -> {
            if (predicate.test(value)) {
                return true;
            } else {
                action.accept(value);
                return false;
            }
        };
    }
    

    then:

    List<Person> filtered = persons.stream()
      .filter(andLogFilteredOutValues(p -> !"John".equals(p.getName()), System.out::println))
      .collect(Collectors.toList());
    
    0 讨论(0)
提交回复
热议问题