使用IntStream快速创建数组的随机子集

时间:2016-04-01 13:00:11

标签: java arrays lambda

我想知道IntStream和lambdas是否可以以某种方式快速(单行)创建一个包含现有元素数组的随机子集的数组。

例如,假设我有一群玩家:

Player[] allPlayers;

考虑到所需子集的维度,我想获得这些玩家的随机子集。传统上我会做类似的事情:

List<Player> players = new ArrayList<Player>(Arrays.asList(allPlayers));

int subsetSize = 8;
Player[] subset = new Player[subsetSize];
for (int i = 0; i < subsetSize; i++) {
  int randIndex = new Random().nextInt(players.size());
  subset[i] = players[randIndex];

  players.remove(randIndex);
}

return subset;

但是这个过程可以用Java 8功能完成吗?我认为这会使它更加浓缩,这正是我想要实现的目标。我仍然掌握着这些新的Java 8功能,例如IntStream和lambdas,我不知道如何在这种情况下使用它们。

3 个答案:

答案 0 :(得分:3)

您可以使用以下内容,它不使用Stream,不是单行,但已经更加精简。

List<Player> copy = new ArrayList<>(Arrays.asList(allPlayers));
Collections.shuffle(copy);
return copy.subList(0, subsetSize).toArray(new Player[0]);

答案 1 :(得分:3)

在这种情况下,您希望从输入数组中选择streamSize个不同的元素。

您可以使用Random#ints(randomNumberOrigin, randomNumberBound)方法:

  

返回一个有效无限的伪随机int值流,每个值都符合给定的原点(包括)和绑定(不包括)。

这将返回指定范围内的随机整数流。为了确保不同的值,我们会调用distinct()limit(...)只允许保留我们想要的元素数。

Random random = new Random();
Player[] subset = random.ints(0, allPlayers.length)
                        .distinct()
                        .limit(subsetSize)
                        .mapToObj(i -> allPlayers[i])
                        .toArray(Player[]::new);

然而,我会注意到,即使这是一个单行,它也不如JB Nizet's solution有效,因为这会继续生成整数,直到找到一个不同的整数。

答案 2 :(得分:2)

有效的方法是:

List<String> players = Arrays.asList("a", "b", "c", "d", "e");
int subsetSize = 3;
for (int i = 0; i < subsetSize; i++)
    Collections.swap(players, i, ThreadLocalRandom.current().nextInt(i, players.size()));
System.out.println(players.subList(0, subsetSize)); 

如果subsetSize很大,这并不需要对未使用的索引进行低效搜索。它并不需要在整个列表中调用shuffle,因此如果subsetSize很小,则更合适。它还避免调用具有线性时间复杂度的ArrayList.remove

此代码不使用Stream操作,但我不认为使用Stream编写简单有效的解决方案而不使用修改状态的lambda(被视为不良做法)。