Java 从顺序集合中获取随机元素
我与一个API交谈,该API为我提供了一个集合上的Java 从顺序集合中获取随机元素,java,iterator,Java,Iterator,我与一个API交谈,该API为我提供了一个集合上的java.util.Iterator。这意味着我可以对它进行迭代,但无法直接/随机访问元素 现在我的问题是:我想从这个集合中获取一个随机元素。我该怎么做?我想我可以建立一个允许直接访问的新集合,但这不是有点消耗内存吗?我还可以对整个集合进行迭代,并对每个元素“掷骰子”,看看是否应该接受该元素并退出迭代或继续。但是我需要集合的大小,我不能从迭代器中得到它 提前感谢。迭代时,您知道迭代了多少对象,因此您知道当前元素是随机选择的元素的概率。因此,您只需
java.util.Iterator
。这意味着我可以对它进行迭代,但无法直接/随机访问元素
现在我的问题是:我想从这个集合中获取一个随机元素。我该怎么做?我想我可以建立一个允许直接访问的新集合,但这不是有点消耗内存吗?我还可以对整个集合进行迭代,并对每个元素“掷骰子”,看看是否应该接受该元素并退出迭代或继续。但是我需要集合的大小,我不能从迭代器中得到它
提前感谢。迭代时,您知道迭代了多少对象,因此您知道当前元素是随机选择的元素的概率。因此,您只需保持计数和当前随机选择的项目
public static <T> T selectRandom(final Iterator<T> iter, final Random random) {
if (!iter.hasNext()) {
throw new IllegalArgumentException();
}
if (random == null) {
throw new NullPointerException();
}
T selected = iter.next();
int count = 1;
while (iter.hasNext()) {
final T current = iter.next();
++count;
if (random.nextInt(count) == 0) {
selected = current;
}
}
return selected;
}
publicstatict selectRandom(最终迭代器iter,最终随机){
如果(!iter.hasNext()){
抛出新的IllegalArgumentException();
}
if(random==null){
抛出新的NullPointerException();
}
T selected=iter.next();
整数计数=1;
while(iter.hasNext()){
最终T电流=iter.next();
++计数;
if(random.nextInt(count)==0){
所选=当前;
}
}
返回选中的;
}
(堆栈溢出免责声明:未编译,当然也未测试。)
另请参见Java拼图游戏中关于集合的部分。shuffle。唯一安全的解决方案(在没有进一步信息的情况下)是您描述的方式:
从迭代器
创建一个列表
,然后选择一个随机元素
如果基础集合的大小始终相同,则平均可以减少一半的工作量-只需在迭代次数随机后使用迭代器.next()之后得到的元素即可
顺便说一句:您真的在使用实现
java.util.Iterator
的集合吗?这取决于需求,如果集合的大小不是很大,那么这就可以了,否则您应该迭代并使用您提到的骰子方法
List<Object> list = Arrays.asList(yourCollection.toArray(new Object[0]));
result = list.get(new Random().nextInt(list.size()));
List List=Arrays.asList(yourCollection.toArray(新对象[0]);
结果=list.get(new Random().nextInt(list.size());
有一种方法可以在通过集合的一次过程中完成,该方法不需要使用大量额外内存(只需集合中一个元素的大小加上一个浮点值)。在伪代码中:
- 遍历集合李>
- 对于每个项目,生成一个随机浮动李>
- 如果浮点值是迄今为止看到的最低值(或最高值,无所谓),则将集合中的当前项存储在临时变量中。(还存储新的最低随机值。)
- 一旦到达集合的末尾,temp变量中就有一个随机项
更新:我终于想起了这类问题的名称。这称为。如果您确实没有任何随机访问权限,并且您有一个非常大的列表,因此无法复制它,则可以执行以下操作:
int n = 2
iterator i = ...
Random rand = new Random();
Object candidate = i.next();
while (i.hasNext()) {
if (rand.nextInt(n)) {
candidate = i.next();
} else {
i.next();
}
n++;
}
return candidate;
这将保留列表中的随机元素,但需要遍历整个列表。如果您想要一个真正均匀分布的值,您别无选择,只能这样做
或者,如果项目的数量很小,或者您希望对未知大小的列表进行随机排列(换句话说,您希望以随机顺序访问列表中的所有元素),那么我建议将所有引用复制到新列表(这不会是很大的内存量,除非您有数百万项,因为您只存储引用)。然后使用随机整数get或使用标准的java.util.Collections shuffle方法来排列列表。使用此方法生成加权测试数据。虽然效率不高,但很容易
class ProbabilitySet<E> {
Set<Option<E>> options = new HashSet<Option<E>>();
class Option<E> {
E object;
double min;
double max;
private Option(E object, double prob) {
this.object = object;
min = totalProb;
max = totalProb + prob;
}
@Override
public String toString() {
return "Option [object=" + object + ", min=" + min + ", max=" + max + "]";
}
}
double totalProb = 0;
Random rnd = new Random();
public void add(E object, double probability){
Option<E> tuple = new Option<E>(object, probability);
options.add(tuple);
totalProb += probability;
}
public E getRandomElement(){
double no = rnd.nextDouble() * totalProb;
for (Option<E> tuple : options) {
if (no >= tuple.min && no < tuple.max){
return tuple.object;
}
}
return null; // if this happens sumfink is wrong.
}
@Override
public String toString() {
return "ProbabilitySet [options=" + options + ", totalProb=" + totalProb + "]";
}
}
类概率集{
Set options=new HashSet();
类选项{
E对象;
双分钟;
双峰;
私有选项(E对象,双prob){
this.object=对象;
最小值=总概率;
最大值=总概率+概率;
}
@凌驾
公共字符串toString(){
返回“Option[object=“+object+”,min=“+min+”,max=“+max+””;
}
}
双总概率=0;
随机rnd=新随机();
公共无效添加(E对象,双重概率){
选项元组=新选项(对象,概率);
添加(元组);
总概率+=概率;
}
公共E getrandom元素(){
double no=rnd.nextDouble()*totalProb;
for(选项元组:选项){
if(no>=tuple.min&&no
注:概率参数将与总量相关,而不是1.0
用法:
public static void main(String[] args) {
ProbabilitySet<String> stati = new ProbabilitySet<String>();
stati.add("TIMEOUT", 0.2);
stati.add("FAILED", 0.2);
stati.add("SUCCESSFUL", 1.0);
for (int i = 0; i < 100; i++) {
System.out.println(stati.getRandomElement());
}
}
publicstaticvoidmain(字符串[]args){
ProbabilitySet stati=新的ProbabilitySet();
统计添加(“超时”,0.2);
统计添加(“失败”,0.2);
统计添加(“成功”,1.0);
对于(int i=0;i<100;i++){
System.out.println(stati.getRandomElement());
}
}
集合通常不应该是实现迭代器的类。您的集合是java.util.collection
?内存消耗不应该太大。新集合只包含指向实际数据的指针,因此新集合对象的大小!=