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

Java8将来自流的随机点与来自其他流的播放器对象相关联,java,java-8,java-stream,Java,Java 8,Java Stream,这真的让我感到困惑。假设我有一个Player对象,其中点p包含x和y值: class Player { void movePlayer(Point p) { ... } } public LongStream allValuesAsLong() { return LongStream.concat(LongStream.concat( LongStream.range(2384, 2413).map(x -> x <&l

这真的让我感到困惑。假设我有一个
Player
对象,其中
点p
包含
x
y
值:

class Player {
    void movePlayer(Point p) {
         ...
    }
}
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());
}
如果我有一堆静态点(当然比玩家多),我需要随机但唯一地映射到每个玩家的movePlayer函数,我会怎么做?这个过程不需要很快完成,但每次都要经常随机完成。为了增加一层复杂性,我的点是由变化的x和y值生成的。到目前为止,我正在执行以下操作(导致JVM崩溃):

现在,当我使用一些荒谬的抽象
,除了它只使用第一个
中的点之外,这几乎奏效了


我是不是完全忽略了溪流的意义?我只是喜欢不创建明确的
对象的想法,我无论如何都不会使用这些对象。

您应该这样做:

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

你应该这样做:

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

您可以定义一个返回
点流的方法,如

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.concat(
   Stream.concat(
      Stream.concat(a, b),
      Stream.concat(c, d)
   ),
   Stream.concat(
      Stream.concat(a, b),
      Stream.concat(c, d)
   )
)
然而,即使这样的流允许在不处理元素的情况下计算大小,这在Java9之前是不会发生的。在Java 8中,
count()
将始终迭代所有元素,这意味着在
count()
操作之后,已经实例化了与将所有元素收集到
列表中时一样多的
实例

更糟糕的是,
skip
不会传播到流的源,因此当说
Stream.map(…).skip(n).findFirst()
时,映射函数的计算次数最多为
n+1次,而不是仅计算一次。当然,这使得
getRandomPoints
方法的整个思想都变得毫无用处。由于这里的封装和嵌套流,我们甚至不能在
映射之前移动
跳过
操作

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

class Player {
    void movePlayer(Point p) {
         ...
    }
}
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());
}
public LongStream allValuesAsLong(){
返回LongStream.concat(LongStream.concat(

LongStream.range(23842413).map(x->x>>32),(int)(l&(1L好的,您可以定义一个方法,返回
点流

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.concat(
   Stream.concat(
      Stream.concat(a, b),
      Stream.concat(c, d)
   ),
   Stream.concat(
      Stream.concat(a, b),
      Stream.concat(c, d)
   )
)
然而,即使这样的流允许在不处理元素的情况下计算大小,这在Java9之前是不会发生的
将始终迭代所有元素,这意味着在执行
count()
操作后,已经实例化了与将所有元素收集到
列表中时一样多的
实例

更糟糕的是,
skip
不会传播到流的源,因此在说
Stream.map(…).skip(n).findFirst()时
,映射函数的计算次数最多为
n+1
次,而不是仅计算一次。当然,这使得
getRandomPoints
方法的整个思想都变得毫无用处。由于这里的封装和嵌套流,我们甚至无法在
ma之前移动
skip
操作p

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

class Player {
    void movePlayer(Point p) {
         ...
    }
}
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());
}
public LongStream allValuesAsLong(){
返回LongStream.concat(LongStream.concat(

LongStream.range(23842413.map(x->x>>32),(int)(l)&(1)很棒的东西!你建议如何建立一个由所有这些随机点组成的流?或者你是否使用了我拥有的?这完全取决于你需要什么,因为你的情况非常具体。你有6个点,必须在它们的特定范围内生成,然后重新排列。一般的随机点流很容易通过extendi实现ng
AbstractSpliterator
。如果我有一个可变数量的玩家怎么办?我需要这个限制吗,因为不计算玩家列表的大小会很好(我的应用程序需要一个循环)。老实说,我根本不明白OP为什么创建一个
列表
,所以你决定只做
findAny()
在这些流上,它将简单地返回这个特定用例中的第一个元素,这样做是正确的。但是,当只考虑这些流的第一个元素时,生成
n
点流仍然没有意义,换句话说,只在第一个位置生成
n
点将产生e完全相同的结果,使整个嵌套流操作过时。唯一的问题是,我的点没有遵循一个简单的趋势(我看到),可以使用一个流创建。为了解决这个问题,我尝试做了一个
flatMap
类似的:
stream()…flatMap(t->t)..
。这使得一个包含所有点的大流而不是一个流的流。您的评论使我认为最好的解决方案可能是定制