根据流中过滤最后一个元素的过滤器做出不同的反应
React differently depending on which filter filtered last element from stream
我尝试根据过滤最后一个元素的过滤器抛出异常:
// Find the first person who is older than 18 and has a pet
Person firstOlderThan18AndWithPet = this.people.entrySet().stream()
.map(e -> e.getValue())
.filter(e -> e.getAge() >= 18)
.findAtLeastOne() // <-- this method doesn't exist
.orElseThrow(AgeException::new) // <-- exception because no person was older than 18
.filter(e -> e.hasPet())
.findFirst()
.orElseThrow(NoPetException::new); // <-- exception because no person older than 18 has a pet
这样我就可以区分为什么在人流中找不到人了。是年龄的原因还是没有人养宠物
我只看到一个选项,却用两个流来做?
List<Person> peopleOlderThan18 = this.people.entrySet().stream()
.map(e -> e.getValue())
.filter(e -> e.getAge() >= 18)
.collect(Collectors.toList());
if (0 == peopleOlderThan18.size()) {
throw new AgeException();
}
Person firstOlderThan18AndWithPet = peopleOlderThan18.stream()
.filter(e -> e.hasPet())
.findFirst()
.orElseThrow(NoPetException::new); // <-- exception because no person older than 18 has a pet
或者我可以做些什么来在一个流中完成所有事情?
无法clone/fork流式传输。您可以组合一系列运算符并仅应用一次。因此,其中一种方法是利用元组来存储中间结果(Java 中没有元组文字)。保存
在不损失代码表现力的情况下,您可以提取通用部分并重新初始化流以应用另一组运算符。
Predicate<Person> olderThan18 = person -> person.age >= 18;
Person firstOlderThan18 = people.stream()
.filter(olderThan18)
.findFirst()
.orElseThrow(Exception::new);
Person firstOlderThan18AndWithPet = people.stream()
.filter(olderThan18)
.filter(person -> person.hasPet)
.findFirst()
.orElseThrow(AssertionError::new);
您必须决定要针对哪种情况进行优化。
通常,我们针对非错误情况进行优化。因此,我们可以乐观地尝试在单次迭代中找到匹配元素,而无需额外存储:
Person firstOlderThan18AndWithPet = this.people.values().stream()
.filter(e -> e.getAge() >= 18 && e.hasPet())
.findFirst()
.orElseThrow(() ->
this.people.values().stream().nonMatch(e -> e.getAge() >= 18)?
new AgeException():
new NoPetException()
);
如果没有找到匹配并且第二次迭代短路,这将只执行第二次迭代。在错误的情况下,一旦我们遇到一个 18 岁以上的人,我们就知道问题是没有候选人有宠物。
我们可以尝试避免第二次迭代,但它会使代码变得更复杂,而只有例外情况才会受益,但代价是非错误情况。
我现在想出了一个解决方案,我可以抛出正确的异常,而无需在中间留下流。我采用的解决方案如下所示:
AtomicBoolean ageMatchedAtLeastOnce = new AtomicBoolean(false);
// Find the first person who is older than 18 and has a pet
Person firstOlderThan18AndWithPet = this.people.entrySet().stream()
.map(e -> e.getValue())
.filter(e -> {
var ageMatch = e.getAge() >= 18
if (ageMatch) {
ageMatchedAtLeastOnce.set(true);
}
return ageMatch;
})
.filter(e -> e.hasPet())
.findFirst()
.orElseThrow(() -> !ageMatchedAtLeastOnce.get() ? new AgeException() : new NoPetException());
我不知道这是否是一个好的解决方案。很高兴在评论中听到您对此的看法。
我尝试根据过滤最后一个元素的过滤器抛出异常:
// Find the first person who is older than 18 and has a pet
Person firstOlderThan18AndWithPet = this.people.entrySet().stream()
.map(e -> e.getValue())
.filter(e -> e.getAge() >= 18)
.findAtLeastOne() // <-- this method doesn't exist
.orElseThrow(AgeException::new) // <-- exception because no person was older than 18
.filter(e -> e.hasPet())
.findFirst()
.orElseThrow(NoPetException::new); // <-- exception because no person older than 18 has a pet
这样我就可以区分为什么在人流中找不到人了。是年龄的原因还是没有人养宠物
我只看到一个选项,却用两个流来做?
List<Person> peopleOlderThan18 = this.people.entrySet().stream()
.map(e -> e.getValue())
.filter(e -> e.getAge() >= 18)
.collect(Collectors.toList());
if (0 == peopleOlderThan18.size()) {
throw new AgeException();
}
Person firstOlderThan18AndWithPet = peopleOlderThan18.stream()
.filter(e -> e.hasPet())
.findFirst()
.orElseThrow(NoPetException::new); // <-- exception because no person older than 18 has a pet
或者我可以做些什么来在一个流中完成所有事情?
无法clone/fork流式传输。您可以组合一系列运算符并仅应用一次。因此,其中一种方法是利用元组来存储中间结果(Java 中没有元组文字)。保存 在不损失代码表现力的情况下,您可以提取通用部分并重新初始化流以应用另一组运算符。
Predicate<Person> olderThan18 = person -> person.age >= 18;
Person firstOlderThan18 = people.stream()
.filter(olderThan18)
.findFirst()
.orElseThrow(Exception::new);
Person firstOlderThan18AndWithPet = people.stream()
.filter(olderThan18)
.filter(person -> person.hasPet)
.findFirst()
.orElseThrow(AssertionError::new);
您必须决定要针对哪种情况进行优化。
通常,我们针对非错误情况进行优化。因此,我们可以乐观地尝试在单次迭代中找到匹配元素,而无需额外存储:
Person firstOlderThan18AndWithPet = this.people.values().stream()
.filter(e -> e.getAge() >= 18 && e.hasPet())
.findFirst()
.orElseThrow(() ->
this.people.values().stream().nonMatch(e -> e.getAge() >= 18)?
new AgeException():
new NoPetException()
);
如果没有找到匹配并且第二次迭代短路,这将只执行第二次迭代。在错误的情况下,一旦我们遇到一个 18 岁以上的人,我们就知道问题是没有候选人有宠物。
我们可以尝试避免第二次迭代,但它会使代码变得更复杂,而只有例外情况才会受益,但代价是非错误情况。
我现在想出了一个解决方案,我可以抛出正确的异常,而无需在中间留下流。我采用的解决方案如下所示:
AtomicBoolean ageMatchedAtLeastOnce = new AtomicBoolean(false);
// Find the first person who is older than 18 and has a pet
Person firstOlderThan18AndWithPet = this.people.entrySet().stream()
.map(e -> e.getValue())
.filter(e -> {
var ageMatch = e.getAge() >= 18
if (ageMatch) {
ageMatchedAtLeastOnce.set(true);
}
return ageMatch;
})
.filter(e -> e.hasPet())
.findFirst()
.orElseThrow(() -> !ageMatchedAtLeastOnce.get() ? new AgeException() : new NoPetException());
我不知道这是否是一个好的解决方案。很高兴在评论中听到您对此的看法。