如何从流api的列表中获取随机元素?

aek*_*ber 16 java java-8

使用Java8流API从列表中获取随机元素的最有效方法是什么?

Arrays.asList(new Obj1(), new Obj2(), new Obj3());
Run Code Online (Sandbox Code Playgroud)

谢谢.

Jea*_*nès 18

为什么有溪流?您只需要从0到列表大小的随机数,然后调用get此索引:

Random r = new Random();
ElementType e = list.get(r.nextInt(list.size()));
Run Code Online (Sandbox Code Playgroud)

Stream会在这里给你带来任何有趣的东西,但你可以尝试:

Random r = new Random();
ElementType e = list.stream().skip(r.nextInt(list.size()-1)).findFirst().get();
Run Code Online (Sandbox Code Playgroud)

想法是跳过任意数量的元素(但不是最后一个元素!),然后获取第一个元素(如果它存在).结果你将得到一个Optional<ElementType>非空的,然后用它提取它的值get.跳过后你有很多选择.

在这里使用流是非常低效的......

注意:这些解决方案都不考虑空列表,但问题是在非空列表中定义的.

  • `list.stream().skip(r.nextInt(list.size()-1)).findFirst().get();` **永远不会选择流中的最后一个元素**。它应该是 `list.stream().skip(r.nextInt(list.size())).findFirst().get();` 因为 `Random.nextInt(5)` 永远不会返回 5。在撰写本文时有 25 个赞成票,我不敢想象有多少正在制作的程序有倾斜的随机选择。 (7认同)

小智 7

虽然所有给出的答案都有效,但有一个简单的单行代码可以做到这一点,而不必先检查列表是否为空:

List<String> list = List.of("a", "b", "c");
list.stream().skip((int) (list.size() * Math.random())).findAny();
Run Code Online (Sandbox Code Playgroud)

对于空列表,这将返回一个Optional.empty.


Ric*_*rdK 5

有很多更有效的方法,但是如果必须使用Stream,最简单的方法是创建自己的Comparator,它返回随机结果(-1、0、1)并对流进行排序:

 List<String> strings = Arrays.asList("a", "b", "c", "d", "e", "f");
    String randomString = strings
            .stream()
            .sorted((o1, o2) -> ThreadLocalRandom.current().nextInt(-1, 2))
            .findAny()
            .get();
Run Code Online (Sandbox Code Playgroud)

ThreadLocalRandom已准备好“开箱即用”的方法来获取比较器所需范围内的随机数。

  • 它破坏了 Comparator::compareTo 契约。 (3认同)
  • 打破 Comparator::compareTo 合约 (3认同)
  • 比较器::compareTo 合约 (3认同)
  • 它打破了。 (3认同)
  • 不错的黑客,但它破坏了 Comparator::compareTo 契约。 (2认同)

adr*_*ony 5

上次我需要做类似的事情时,我这样做了:

List<String> list = Arrays.asList("a", "b", "c");
Collections.shuffle(list);
String letter = list.stream().findAny().orElse(null);
System.out.println(letter);
Run Code Online (Sandbox Code Playgroud)