尝试将嵌套的 for 循环表示为 Java Stream,但感到困惑。

huangapple go评论69阅读模式
英文:

Trying to represent a nested for loop as a Java Stream, but confused

问题

我正试图更好地理解 Java 中的流(streams),我只是试图将一个嵌套的 for 循环表示为一个流,但我难以弄清楚。

我尝试了一些方法,但似乎无法掌握。

例如,像这样的一个例子。

for (Profile profile : profiles) {
    for (User user : users) {
        if (user.getUserId().equals(profile.getProfileId())) {
           profile.setField(false);
        }
    }
}

在 Java 中如何使用流来实现这个呢?

英文:

Im trying to understand streams in java better, and im just trying to represent a nested for loop as a stream but im struggling to figure it out.

Ive tried a handful of things, but I cant seem to grasp it.

For example, something like this.

for (Profile profile : profiles) {
    for (User user : users) {
        if (user.getUserId().equals(profile.getProfileId())) {
           profile.setField(false);
        }
    }
}

How would one do this as a stream in Java?

答案1

得分: 5

我认为您正在尝试理解流(Stream),但请让我说一下,for 版本并不差,Stream 版本也不一定更好:性能测试和直觉可能会对您有所帮助,以及可读性。

话虽如此,您在这里的问题是,您的第二种方法需要第一种方法才能正常工作:

profiles.stream()
        .filter(profile -> users.stream() 
                                .anyMatch(user -> user.getUserId().equals(profile.getProfileId())))
        .forEach(profile -> profile.setField(false));

这与您的 for 循环严格来说并不相同:您为每个配置文件多次调用 setField 方法,而在流版本中,只有在有一个匹配的用户时才会调用一次。我假设 setField 是一个 setter 方法,因此由于其值是常量,因此是否调用一次或多次并不重要。

我建议您在这种情况下不要使用第一个流,或者仅限于使用 forEach

profiles.forEach(profile -> profile.setField(!users.stream() 
                                   .anyMatch(user -> user.getUserId().equals(profile.getProfileId()))));

这可以通过首先获取所有用户ID并使用生成的集合的 contains 方法来简化:

var userIds = users.stream().map(User::getUserId).collect(toSet());
profiles.forEach(profile -> profile.setField(!userIds.contains(profile.getProfileId())));
英文:

I think you are trying to understand stream, but let me say that the for version is not worse and the Stream version is not better: performance test and intuition will probably help you there, as well as readability.

That said, you problem here is that your second look require the first one to work:

profiles.stream()
        .filter(profile -> users.stream() 
                                .anyMatch(user -> user.getUserId().equals(profile.getProfileId())))
        .forEach(profile -> profile.setField(false));

This is strictly not the same as your for loop: you call setField several time per profile, while in the stream version it is only ever done if there is one matching user. I assumed that setField is a setter, and as such since its value is constant, it should not matter if it is called once or several times.

I would advise you not to use the first stream in this case, or limit yourself to forEach:

profiles.forEach(profile -> profile.setField(!users.stream() 
                                   .anyMatch(user -> user.getUserId().equals(profile.getProfileId())));

Which could be simplified by first getting all user ids and using contains from the generated set:

   var userIds = users.stream().map(User::getUserId).collect(toSet());
   profiles.forEach(profile -> profile.setField(!userIds.contains(profile.getProfileId()));

huangapple
  • 本文由 发表于 2020年9月11日 04:13:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/63837076.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定