我想知道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,我不知道如何在这种情况下使用它们。
答案 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(被视为不良做法)。