了解以函数作为参数的Java流

n53p2ov0  于 2022-11-27  发布在  Java
关注(0)|答案(2)|浏览(119)

我在我的项目中使用Java。我看到下面的代码,但不能理解流程。

public static void main(String[] args) {
    Person p1 = new Person("test1");
    Person p2 = new Person("test2");

    List<Person> l = List.of(p1, p2);
    var count = l.stream().filter(checkMethod(Person::getName)).count();
    System.out.println(count);
  }

  public static final <T> Predicate<T> checkMethod(Function<? super T, ?> keyExtractor) {
    Objects.requireNonNull(keyExtractor);
    final var seen = ConcurrentHashMap.newKeySet();
    return t -> seen.add(keyExtractor.apply(t));
  }

Person.java
 public class Person {

  Person(String name) {
    this.name = name;
  }

  private String name;
   //getters and setters for name
  }
}

它标识了来自person对象列表的唯一名称的计数。我看到check方法只被调用一次。但是通常filter方法将对列表中的每个项目执行。
是不是整个人员列表都被发送到check方法,并且只调用一次?

ljo96ir5

ljo96ir51#

让我们来看看.filter(checkMethod(Person::getName))实际上是做什么的,以及它是如何工作的:

  • 首先执行checkMethod(Person::getName)并返回一个Predicate,这是filter(..)方法所需的参数
  • 然后,对流中的每个项执行filter(t -> seen.add(t.getName()))
  • 由于Collection.add(..)返回一个布尔值,指示是否添加了元素,因此删除了名称已被看到的每个项
monwx1rj

monwx1rj2#

checkMethod()生成一个 * 有状态 * predicate 。
在执行checkMethod()时,JVM计算lambda表达式(* lambda始终在运行时求值 *)并创建 * 函数接口 * 的示例(Predicate),其捕获对Setseen的引用。即,当该方法终止时,该方法中的局部变量seen将消失,但是Predicate将保持对该对象的引用,因此Set在 predicate 活动之前是活动的。
但通常filter方法将对列表中的每个项执行。
每个流元素都将根据checkMethod()使用Predicate.test()返回的Predicate示例进行检查。
换句话说,方法test()将在流中的每个元素的 predicate 上被调用,但是checkMethod()的调用将仅发生一次,并且每个元素将针对相同的 predicate 被检查。
因此,您混淆了通过checkMethod()创建Predicate和通过Predicate.test()验证元素。

相关问题