使用 Java 8 Streams API 打乱整数列表
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20058366/
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
Shuffle a list of integers with Java 8 Streams API
提问by deamon
I tried to translate the following line of Scala to Java 8 using the Streams API:
我尝试使用 Streams API 将以下 Scala 行转换为 Java 8:
// Scala
util.Random.shuffle((1 to 24).toList)
To write the equivalent in Java I created a range of integers:
为了在 Java 中编写等价物,我创建了一系列整数:
IntStream.range(1, 25)
I suspected to find a toList
method in the stream API, but IntStream
only knows the strange method:
我怀疑是toList
在stream API中找到了一个方法,但是IntStream
只知道奇怪的方法:
collect(
Supplier<R> supplier, ObjIntConsumer<R> accumulator, BiConsumer<R,R> combiner)
How can I shuffle a list with Java 8 Streams API?
如何使用 Java 8 Streams API 打乱列表?
采纳答案by Andrey Chaschev
Here you go:
干得好:
List<Integer> integers =
IntStream.range(1, 10) // <-- creates a stream of ints
.boxed() // <-- converts them to Integers
.collect(Collectors.toList()); // <-- collects the values to a list
Collections.shuffle(integers);
System.out.println(integers);
Prints:
印刷:
[8, 1, 5, 3, 4, 2, 6, 9, 7]
回答by Peter Lawrey
To perform a shuffle efficiently you need all the values in advance. You can use Collections.shuffle() after you have converted the stream to a list like you do in Scala.
要有效地执行 shuffle,您需要提前获得所有值。您可以像在 Scala 中那样将流转换为列表后使用 Collections.shuffle()。
回答by Xavier
You can use a custom comparator that "sorts" the values by a random value:
您可以使用自定义比较器按随机值对值进行“排序”:
public final class RandomComparator<T> implements Comparator<T> {
private final Map<T, Integer> map = new IdentityHashMap<>();
private final Random random;
public RandomComparator() {
this(new Random());
}
public RandomComparator(Random random) {
this.random = random;
}
@Override
public int compare(T t1, T t2) {
return Integer.compare(valueFor(t1), valueFor(t2));
}
private int valueFor(T t) {
synchronized (map) {
return map.computeIfAbsent(t, ignore -> random.nextInt());
}
}
}
Each object in the stream is (lazily) associated a random integer value, on which we sort. The synchronization on the map is to deal with parallel streams.
流中的每个对象都(懒惰地)关联一个随机整数值,我们对其进行排序。映射上的同步是处理并行流。
You can then use it like that:
然后你可以像这样使用它:
IntStream.rangeClosed(0, 24).boxed()
.sorted(new RandomComparator<>())
.collect(Collectors.toList());
The advantage of this solution is that it integrates within the stream pipeline.
该解决方案的优势在于它集成在流管道中。
回答by Paul Boddington
You may find the following toShuffledList()
method useful.
您可能会发现以下toShuffledList()
方法很有用。
private static final Collector<?, ?, ?> SHUFFLER = Collectors.collectingAndThen(
Collectors.toCollection(ArrayList::new),
list -> {
Collections.shuffle(list);
return list;
}
);
@SuppressWarnings("unchecked")
public static <T> Collector<T, ?, List<T>> toShuffledList() {
return (Collector<T, ?, List<T>>) SHUFFLER;
}
This enables the following kind of one-liner:
这可以实现以下类型的单行:
IntStream.rangeClosed('A', 'Z')
.mapToObj(a -> (char) a)
.collect(toShuffledList())
.forEach(System.out::print);
Example output:
示例输出:
AVBFYXIMUDENOTHCRJKWGQZSPL
回答by kozla13
This is my one line solution: I am picking one random color:
这是我的单行解决方案:我选择一种随机颜色:
colourRepository.findAll().stream().sorted((o1,o2)-> RandomUtils.nextInt(-1,1)).findFirst().get()
回答by sigpwned
If you're looking for a "streaming only" solution and a deterministic, merely "haphazard" ordering versus a "random" ordering is Good Enough, you can always sort your int
s by a hash value:
如果您正在寻找“仅流媒体”解决方案,并且确定性的、仅仅是“随意”的排序与“随机”的排序就足够了,那么您始终可以int
按哈希值对s 进行排序:
List<Integer> xs=IntStream.range(0, 10)
.boxed()
.sorted( (a, b) -> a.hashCode() - b.hashCode() )
.collect(Collectors.toList());
If you'd rather have an int[]
than a List<Integer>
, you can just unbox them afterwards. Unfortunately, you have go through the boxing step to apply a custom Comparator
, so there's no eliminating that part of the process.
如果您更喜欢 an 而int[]
不是 a List<Integer>
,您可以在之后将它们拆箱。不幸的是,您已经完成了装箱步骤以应用 custom Comparator
,因此无法消除该过程的那部分。
List<Integer> ys=IntStream.range(0, 10)
.boxed()
.sorted( (a, b) -> a.hashCode() - b.hashCode() )
.mapToInt( a -> a.intValue())
.toArray();
回答by Grzegorz Piwowarek
If you want to process the whole Stream without too much hassle, you can simply create your own Collector using Collectors.collectingAndThen()
:
如果您想轻松处理整个 Stream,您可以简单地使用Collectors.collectingAndThen()
以下命令创建自己的收集器:
public static <T> Collector<T, ?, Stream<T>> toEagerShuffledStream() {
return Collectors.collectingAndThen(
toList(),
list -> {
Collections.shuffle(list);
return list.stream();
});
}
But this won't perform well if you want to limit()
the resulting Stream. In order to overcome this, one could create a custom Spliterator:
但是,如果您想要limit()
生成的 Stream,这将不会表现良好。为了克服这一点,可以创建一个自定义 Spliterator:
package com.pivovarit.stream;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.RandomAccess;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.Supplier;
class ImprovedRandomSpliterator<T, LIST extends RandomAccess & List<T>> implements Spliterator<T> {
private final Random random;
private final List<T> source;
private int size;
ImprovedRandomSpliterator(LIST source, Supplier<? extends Random> random) {
Objects.requireNonNull(source, "source can't be null");
Objects.requireNonNull(random, "random can't be null");
this.source = source;
this.random = random.get();
this.size = this.source.size();
}
@Override
public boolean tryAdvance(Consumer<? super T> action) {
if (size > 0) {
int nextIdx = random.nextInt(size);
int lastIdx = --size;
T last = source.get(lastIdx);
T elem = source.set(nextIdx, last);
action.accept(elem);
return true;
} else {
return false;
}
}
@Override
public Spliterator<T> trySplit() {
return null;
}
@Override
public long estimateSize() {
return source.size();
}
@Override
public int characteristics() {
return SIZED;
}
}
and then:
进而:
public final class RandomCollectors {
private RandomCollectors() {
}
public static <T> Collector<T, ?, Stream<T>> toImprovedLazyShuffledStream() {
return Collectors.collectingAndThen(
toCollection(ArrayList::new),
list -> !list.isEmpty()
? StreamSupport.stream(new ImprovedRandomSpliterator<>(list, Random::new), false)
: Stream.empty());
}
public static <T> Collector<T, ?, Stream<T>> toEagerShuffledStream() {
return Collectors.collectingAndThen(
toCollection(ArrayList::new),
list -> {
Collections.shuffle(list);
return list.stream();
});
}
}
I explained the performance considerations here: https://4comprehension.com/implementing-a-randomized-stream-spliterator-in-java/
我在这里解释了性能考虑:https: //4comprehension.com/implementing-a-randomized-stream-spliterator-in-java/
回答by RichardK
public static List<Integer> getSortedInRandomOrder(List<Integer> list) {
return list
.stream()
.sorted((o1, o2) -> ThreadLocalRandom.current().nextInt(-1, 2))
.collect(Collectors.toList());
}