我代表一组海龟,每只海龟都有孵化的时刻。我想随机选择任何一只成熟的海龟。
在这个水族馆模拟中,我假装几分钟就是几年。
record Turtle(
String name ,
Instant hatched ,
Duration lifespan
)
{
static final Duration MATURITY = Duration.ofMinutes ( 4 );
Duration age ( ) { return Duration.between ( this.hatched , Instant.now ( ) ); }
}
我用来ThreadLocalRandom
生成一个随机整数。我使用这些整数作为我的对象IntStream
的索引。我检查那只海龟的年龄,看它是否超过了预定义的持续时间。如果没有成熟,就让流继续。List
Turtle
MATURITY
当然,可能还没有海龟成熟。所以我将返回类型定义为Optional < Turtle >
,如果没有找到海龟,则为空。
问题是我的流似乎失败了,没有返回任何结果。为什么?
public class Aquarium
{
public static void main ( String[] args )
{
System.out.println ( "INFO Demo start. " + Instant.now ( ) );
// Sample data.
List < Turtle > turtles =
List.of (
new Turtle ( "Alice" , Instant.now ( ).truncatedTo ( ChronoUnit.MINUTES ).minus ( Duration.ofMinutes ( 3 ) ) , Duration.ofMinutes ( 17 ) ) ,
new Turtle ( "Bob" , Instant.now ( ).truncatedTo ( ChronoUnit.MINUTES ).minus ( Duration.ofMinutes ( 2 ) ) , Duration.ofMinutes ( 16 ) ) ,
new Turtle ( "Carol" , Instant.now ( ).truncatedTo ( ChronoUnit.MINUTES ).minus ( Duration.ofMinutes ( 1 ) ) , Duration.ofMinutes ( 18 ) ) ,
new Turtle ( "Davis" , Instant.now ( ).truncatedTo ( ChronoUnit.MINUTES ).minus ( Duration.ofMinutes ( 2 ) ) , Duration.ofMinutes ( 22 ) )
);
System.out.println ( "turtles = " + turtles );
// Logic
Optional < Turtle > anArbitraryMatureTurtle =
ThreadLocalRandom
.current ( )
.ints ( 0 , turtles.size ( ) )
.filter (
( int randomIndex ) -> turtles.get ( randomIndex ).age ( ).compareTo ( Turtle.MATURITY ) > 0
)
.mapToObj ( turtles :: get )
.findAny ( );
System.out.println ( "anArbitraryMatureTurtle = " + anArbitraryMatureTurtle );
// Wrap-up
try { Thread.sleep ( Duration.ofMinutes ( 30 ) ); } catch ( InterruptedException e ) { throw new RuntimeException ( e ); } // Let the aquarium run a while.
System.out.println ( "INFO Demo end. " + Instant.now ( ) );
}
}
Infinite
Stream
可以无限循环您无意中使无限陷入了无限循环
IntStream
。您
IntStream
创建的ThreadLocalRandom.ints
是无限的,会在您定义的范围内不断生成一个又一个随机数。可能会出现重复,因为随机数会不断生成,直到您的流达到终止操作。您调用
findAny
终止流。但是查看您的示例数据。您的所有海龟都很年轻,没有一只海龟的年龄符合您的IntPredicate
超过MATURITY
持续时间的测试。因此,您的所有海龟都没有通过年龄测试。因此,findAny
永远不会找到任何Turtle
,因此它永远不会返回任何Optional < Turtle >
。这意味着随机数生成不断进行,产生一个又一个索引号。对于每个随机数,我们访问
List
对象Turtle
。但没有一个通过我们的谓词测试。因此,我们的流永不终止。这个随机生成器 ->List#get
-> 谓词测试程序无休止地继续。在这个无限循环中,流永不终止,看起来什么都没有发生。实际上,发生了很多事情,一个非常繁忙的 CPU 核心生成数字、访问列表并计算和比较持续时间,无休止地进行。我们可以通过更改样本数据来证明这一点。启动
Bob
&Davis
鱼,孵化日期为 7 分钟前,而不是 2 分钟前。再次运行代码,看看它们是否都是随机挑选的,而且挑选速度非常快。解决方案
没有解决方案可以直接修复当前代码†。如果没有保证终止,则不能使用无限流。
相反,您必须重写代码。
一种重写可能是复制您的
List
对象Turtle
。对它们进行打乱,将列表按随机顺序排列。然后流式传输该随机列表,直到找到一个满足谓词测试条件的列表。如果没有满足这些条件的列表,则在详尽检查每个元素后流式传输结束,并Optional
返回空值。将您的逻辑部分更改为:
不再有随机索引。
当与所有未成熟的幼龟一起运行时:
相同的逻辑可以用常规
for
循环而不是来实现Stream
。我发现在Stream
这个特定情况下语法更具表现力。对整个集合进行混洗可能不如随机选择索引那么高效。这可能是您选择随机索引方法的最初动机。但考虑到您可能的数据值,随机索引方法是不可行的。
†错误……有一个解决方案可以解决该随机索引数字流方法。请参阅tquadrat 的正确答案。
尝试在方法的逻辑部分中添加此操作,添加对和的
main()
调用。这样,我们以随机顺序覆盖所有可能的索引值一次。Stream#distinct
Stream#limit
如果没有
Turtle
通过测试,我们得到一个空的Optional
。代码序列:
0
…以随机顺序打印五个值4
– 始终为五个,没有重复。当然,您必须turtles.size()
根据用例使用 而不是常量5
。我有些怀疑这是从您的列表中获取随机成年乌龟的最有效方法(假设有一只),但它确实有效。