Java 如何从流中获取随机对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31703226/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get random objects from a stream
提问by Adrian Krebs
Lets say I have a list of words and i want to create a method which takes the size of the new list as a parameter and returns the new list. How can i get random words from my original sourceList?
假设我有一个单词列表,我想创建一个方法,该方法将新列表的大小作为参数并返回新列表。如何从原始 sourceList 中获取随机单词?
public List<String> createList(int listSize) {
Random rand = new Random();
List<String> wordList = sourceWords.
stream().
limit(listSize).
collect(Collectors.toList());
return wordList;
}
So how and where can I use my Random?
那么我如何以及在哪里可以使用我的随机?
采纳答案by Adrian Krebs
I've found a proper solution. Random provides a few methods to return a stream. For example ints(size) which creates a stream of random integers.
我找到了一个合适的解决方案。Random 提供了一些方法来返回流。例如 ints(size) 创建一个随机整数流。
public List<String> createList(int listSize)
{
Random rand = new Random();
List<String> wordList = rand.
ints(listSize, 0, sourceWords.size()).
mapToObj(i -> sourceWords.get(i)).
collect(Collectors.toList());
return wordList;
}
回答by mate00
Try something like that:
尝试这样的事情:
List<String> getSomeRandom(int size, List<String> sourceList) {
List<String> copy = new ArrayList<String>(sourceList);
Collections.shuffle(copy);
List<String> result = new ArrayList<String>();
for (int i = 0; i < size; i++) {
result.add(copy.get(i));
}
return result;
}
回答by Timofey
The answer is very simple(with stream):
答案很简单(使用流):
List<String> a = src.stream().sorted((o1, o2) -> {
if (o1.equals(o2)) return 0;
return (r.nextBoolean()) ? 1 : -1;
}).limit(10).collect(Collectors.toList());
You can test it:
你可以测试一下:
List<String> src = new ArrayList<String>();
for (int i = 0; i < 20; i++) {
src.add(String.valueOf(i*10));
}
Random r = new Random();
List<String> a = src.stream().sorted((o1, o2) -> {
if (o1.equals(o2)) return 0;
return (r.nextBoolean()) ? 1 : -1;
}).limit(10).collect(Collectors.toList());
System.out.println(a);
回答by daShader
I think the most elegant way is to have a special collector.
我认为最优雅的方式是拥有一个特殊的收藏家。
I am pretty sure the only way you can guarantee that each item has an equal chance of being picked, is to collect, shuffle and re-stream. This can be easily done using built-in Collectors.collectingAndThen(...) helper.
我很确定你能保证每件物品被挑选的机会均等的唯一方法是收集、洗牌和重新播放。这可以使用内置的 Collectors.collectingAndThen(...) 助手轻松完成。
Sorting by a random comparator or using randomized reducer, like suggested on some other answers, will result in very biased randomness.
通过随机比较器或使用随机减速器进行排序,就像在其他一些答案中建议的那样,将导致非常有偏见的随机性。
List<String> wordList = sourceWords.stream()
.collect(Collectors.collectingAndThen(Collectors.toList(), collected -> {
Collections.shuffle(collected);
return collected.stream();
}))
.limit(listSize)
.collect(Collectors.toList());
You can move that shuffling collector to a helper function:
您可以将该改组收集器移动到辅助函数:
public class CollectorUtils {
public static <T> Collector<T, ?, Stream<T>> toShuffledStream() {
return Collectors.collectingAndThen(Collectors.toList(), collected -> {
Collections.shuffle(collected);
return collected.stream();
});
}
}
I assume that you are looking for a way to nicely integrate with other stream processing functions. So following straightforward solution is not what you are looking for :)
我假设您正在寻找一种与其他流处理功能很好地集成的方法。因此,以下简单的解决方案不是您要寻找的:)
Collections.shuffle(wordList)
return wordList.subList(0, limitSize)
回答by Trejkaz
Here's a solution I came up with which seems to differ from all the other ones, so I figured why not add it to the pile.
这是我想出的一个解决方案,它似乎与所有其他解决方案不同,所以我想为什么不把它添加到一堆。
Basically it works by using the same kind of trick as one iteration of Collections.shuffle
each time you ask for the next element - pick a random element, swap that element with the first one in the list, move the pointer forwards. Could also do it with the pointer counting back from the end.
基本上它通过使用与Collections.shuffle
每次请求下一个元素时的一次迭代相同的技巧来工作- 选择一个随机元素,将该元素与列表中的第一个元素交换,向前移动指针。也可以用指针从末尾开始倒数。
The caveat is that it does mutate the list you passed in, but I guess you could just take a copy as the first thing if you didn't like that. We were more interested in reducing redundant copies.
需要注意的是,它确实会改变您传入的列表,但我想如果您不喜欢那样,您可以将副本作为第一件事。我们更感兴趣的是减少冗余副本。
private static <T> Stream<T> randomStream(List<T> list)
{
int characteristics = Spliterator.SIZED;
// If you know your list is also unique / immutable / non-null
//int characteristics = Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.SIZED;
Spliterator<T> spliterator = new Spliterators.AbstractSpliterator<T>(list.size(), characteristics)
{
private final Random random = new SecureRandom();
private final int size = list.size();
private int frontPointer = 0;
@Override
public boolean tryAdvance(Consumer<? super T> action)
{
if (frontPointer == size)
{
return false;
}
// Same logic as one iteration of Collections.shuffle, so people talking about it not being
// fair randomness can take that up with the JDK project.
int nextIndex = random.nextInt(size - frontPointer) + frontPointer;
T nextItem = list.get(nextIndex);
// Technically the value we end up putting into frontPointer
// is never used again, but using swap anyway, for clarity.
Collections.swap(list, nextIndex, frontPointer);
frontPointer++;
// All items from frontPointer onwards have not yet been chosen.
action.accept(nextItem);
return true;
}
};
return StreamSupport.stream(spliterator, false);
}
回答by kozla13
This is my one line solution:
这是我的单行解决方案:
List<String> st = Arrays.asList("aaaa","bbbb","cccc");
st.stream().sorted((o1, o2) -> RandomUtils.nextInt(0, 2)-1).findFirst().get();
RandomUtils are from commons lang 3
RandomUtils 来自 commons lang 3