Java8将来自流的随机点与来自其他流的播放器对象相关联

时间:2016-07-08 00:25:16

标签: java java-8 java-stream

所以这真让我感到困惑。假设我有Player个对象,Point p包含xy值:

class Player {
    void movePlayer(Point p) {
         ...
    }
}

如果我有一堆静态点(当然比玩家更多),我需要随机地,但唯一地,映射到每个玩家的movePlayer函数,我该怎么做?这个过程不需要快速完成,但每次都经常随机完成。要添加一个复杂的图层,我的点是由变化的x和y值生成的。截至目前,我正在做以下事情(这使我的JVM崩溃了):

public List<Stream<Point>> generatePointStream() {
    Random random = new Random();
    List<Stream<Point>> points = new ArrayList<Stream<Point>>();
    points.add(random.ints(2384, 2413).distinct().mapToObj(x -> new Point(x, 3072)));
    points.add(random.ints(3072, 3084).distinct().mapToObj(y -> new Point(2413, y)));
    ....
    points.add(random.ints(2386, 2415).distinct().mapToObj(x -> new Point(x, 3135)));
    Collections.shuffle(points);
    return points;
}

请注意,在我仅使用Stream.concat方法的一个流之前,但是这会导致错误并且看起来非常丑陋,导致我陷入目前的困境。并将它们分配给List<Player> players中的所有Player对象:

players.stream().forEach(p->p.movePlayer(generatePointStream().stream().flatMap(t->t).
                    findAny().orElse(new Point(2376, 9487))));

现在,当我使用一些荒谬的抽象Stream<Stream<Point>>时,这几乎起作用,除了它只使用了第一个Stream<Point>中的点。

我在这里完全忽略了溪流的意义吗?我只是喜欢不创建我不会使用的显式Point对象的想法。

2 个答案:

答案 0 :(得分:2)

您应该执行以下操作:

final int PLAYERS_COUNT = 6;
List<Point> points = generatePointStream()
                     .stream()
                     .limit(PLAYERS_COUNT)
                     .map(s -> s.findAny().get())
                     .collect(Collectors.toList());

此输出

2403, 3135
2413, 3076
2393, 3072
2431, 3118
2386, 3134
2368, 3113

答案 1 :(得分:2)

好吧,你可以定义一个返回Point的流的方法,如

public Stream<Point> allValues() {
    return Stream.of(
      IntStream.range(2384, 2413).mapToObj(x -> new Point(x, 3072)),
      IntStream.range(3072, 3084).mapToObj(y -> new Point(2413, y)),
    //...
      IntStream.range(2386, 2415).mapToObj(x -> new Point(x, 3135))
    ).flatMap(Function.identity());
}

包含所有有效点,但由于Stream的惰性而未实现。然后,创建一个方法来选择随机元素,如:

public List<Point> getRandomPoints(int num) {
    long count=allValues().count();

    assert count > num;

    return new Random().longs(0, count)
        .distinct()
        .limit(num)
        .mapToObj(i -> allValues().skip(i).findFirst().get())
        .collect(Collectors.toList());
}

在一个完美的世界中,这已经拥有了你想要的所有懒惰,包括只创建所需数量的Point个实例。

但是,有几个实现细节可能会使这比仅仅收集到列表更糟糕。

其中一个对flatMap操作很特殊,请参阅“Why filter() after flatMap() is “not completely” lazy in Java streams?”。不仅要急切处理子流,还要评估可能允许内部优化的流属性。在这方面,基于concat的流更有效。

public Stream<Point> allValues() {
    return Stream.concat(
        Stream.concat(
            IntStream.range(2384, 2413).mapToObj(x -> new Point(x, 3072)),
            IntStream.range(3072, 3084).mapToObj(y -> new Point(2413, y))
        ),
    //...
        IntStream.range(2386, 2415).mapToObj(x -> new Point(x, 3135))
    );
}

关于创建过于深层的连接流有一个警告,但如果你像这里一样控制创建,你可以创建一个平衡的树,比如

Stream.concat(
   Stream.concat(
      Stream.concat(a, b),
      Stream.concat(c, d)
   ),
   Stream.concat(
      Stream.concat(a, b),
      Stream.concat(c, d)
   )
)

然而,即使这样的Stream允许在没有处理元素的情况下计算大小,但这在Java 9之前不会发生。在Java 8中,count()将始终迭代所有元素,这意味着已经实例化为在Point操作之后将所有元素收集到List时的count()个实例很多。

更糟糕的是,skip没有传播到Stream的源代码,因此在说stream.map(…).skip(n).findFirst()时,映射函数的评估时间最多为n+1次而不是一次。当然,这使得使用它作为惰性构造的getRandomPoints方法的整个想法变得毫无用处。由于封装和我们在这里的嵌套流,我们甚至无法在skip之前移动map操作。

请注意,临时实例仍然可能比收集到列表更有效,其中所有实例同时存在,但由于我们在这里有更大的数字,因此很难预测。因此,如果实例创建确实是一个问题,我们可以解决这个特定情况,因为构成一个点的两个int值可以封装在一个原始的long值中:

public LongStream allValuesAsLong() {
    return LongStream.concat(LongStream.concat(
      LongStream.range(2384, 2413).map(x -> x     <<32 | 3072),
      LongStream.range(3072, 3084).map(y -> 2413L <<32 | y)
    ),
    //...
      LongStream.range(2386, 2415).map(x -> x     <<32 | 3135)
    );
}
public List<Point> getRandomPoints(int num) {
    long count=allValuesAsLong().count();

    assert count > num;

    return new Random().longs(0, count)
        .distinct()
        .limit(num)
        .mapToObj(i -> allValuesAsLong().skip(i)
            .mapToObj(l -> new Point((int)(l>>>32), (int)(l&(1L<<32)-1)))
            .findFirst().get())
        .collect(Collectors.toList());
}

这确实只会创建num的{​​{1}}个实例。