从集合中获取随机元素

Sec*_*ret 8 java

我有一个Collection<Obj>如何随机Obj获取它?

我已经检查了文档,似乎没有办法,因为迭代器是访问集合的唯一方法.我是否必须迭代才能获得随机对象!?

Wit*_*rba 30

使用Lambdas,您可以非常快速地执行此操作,并在Collection为空时处理该情况.

public static <E> Optional<E> getRandom (Collection<E> e) {

    return e.stream()
            .skip((int) (e.size() * Math.random()))
            .findFirst();
}
Run Code Online (Sandbox Code Playgroud)

  • 喜欢这个答案,但对其做了一个小调整:`skip(new Random().nextInt(collection.size()))`而不是`skip((int) (e.size() * Math.random()) )` (2认同)

Pet*_*rey 24

最有效的只能根据您的需要进行迭代.

public static <T> T random(Collection<T> coll) {
    int num = (int) (Math.random() * coll.size());
    for(T t: coll) if (--num < 0) return t;
    throw new AssertionError();
}
Run Code Online (Sandbox Code Playgroud)


Aut*_*rab 6

private Object getRandomObject(Collection from) {
   Random rnd = new Random();
   int i = rnd.nextInt(from.size());
   return from.toArray()[i];
}
Run Code Online (Sandbox Code Playgroud)

  • 将整个集合放入数组只是为了获得单个元素似乎有些浪费. (16认同)
  • @Sridhar-Sarnobat:不,nextInt(max)返回从"0"到"max-1"的值. (3认同)
  • 这永远不会返回集合中的最后一项。你应该使用 `rnd.nextInt(from.size())`。 (2认同)

Dav*_*vid 5

我知道这是一个旧线程,但令我惊讶的是没有人提到将实现RandomAccess标记List为通过索引进行非常快速访问的接口。

这是RandomAccess的文档:

List 实现使用标记接口来指示它们支持快速(通常是恒定时间)随机访问。该接口的主要目的是允许通用算法改变其行为,以便在应用于随机或顺序访问列表时提供良好的性能。

例如: 它是通过ArrayList对比实现的LinkedList

这是我利用它的解决方案:

public static <E> E getRandomElement(Collection<E> collection) 
{
    if(collection.isEmpty()) 
        throw new IllegalArgumentException("Cannot return a random value from an empty collection!");

    int randomIndex = ThreadLocalRandom.current().nextInt(collection.size());

    if(collection instanceof RandomAccess) 
        return ((List<E>) collection).get(randomIndex);

    for(E element : collection)
    {
        if(randomIndex == 0)
            return element;

        randomIndex--;
    }

    throw new IllegalStateException("How did we get here?"); //unreachable
}
Run Code Online (Sandbox Code Playgroud)