如何在Java Streams中记录过滤的值

时间:2018-05-24 05:05:48

标签: java java-8 java-stream

我需要log/sysout Java Streams中的过滤值。我可以使用log/sysout方法peek()未过滤的值。 但是,有人可以告诉我如何记录过滤后的值吗?

例如,假设我有一个Person对象列表,如下所示:

List<Person> persons = Arrays.asList(new Person("John"), new Person("Paul"));

我想过滤掉那些不是“约翰”的人如下:

persons.stream().filter(p -> !"John".equals(p.getName())).collect(Collectors.toList());

但是,我必须记录被过滤的“John”人的详细信息。有人可以帮助我实现这个目标吗?

3 个答案:

答案 0 :(得分:12)

如果您想将其与Stream API集成,那么除了手动引入日志之外,您所做的事情并不多。最安全的方法是在filter()方法本身中引入日志记录:

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());

请注意,向Stream API引入副作用是阴暗的,您需要了解自己正在做的事情。

您还可以构建一个通用的包装器解决方案:

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

然后简单地说:

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());

...甚至可以自定义动作:

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;
        }
    };
}

然后:

List<Person> filtered = persons.stream()
  .filter(andLogFilteredOutValues(p -> !"John".equals(p.getName()), System.out::println))
  .collect(Collectors.toList());

答案 1 :(得分:12)

您可以使用

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);

或者,如果您更喜欢单一陈述形式:

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);
        }));

答案 2 :(得分:6)

由于无法对匹配相同流上相反过滤器的元素运行终端操作,因此最佳选择可能只是在peek消费者中使用条件。 / p>

避免遍历流/集合两次。

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());
};

然后我们可以将该消费者传递给peek,之后仅过滤最终动作:

List<Person> nonJohns = persons.stream()
                  .peek(logger)
                  .filter(p -> !"John".equals(p.getName()))
                  .collect(Collectors.toList());