如果你想在不太麻烦的情况下处理整个流,你可以使用 Collectors.collectingAndThen()
创建自己的收集器:
public static <T> Collector<T, ?, Stream<T>> toEagerShuffledStream() {
return Collectors.collectingAndThen(
toList(),
list -> {
Collections.shuffle(list);
return list.stream();
});
}
但是,如果你想要对结果Stream进行limit()
操作,则效果可能不佳。为了克服这个问题,可以创建一个自定义的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;
}
}
接着:
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();
});
}
}
我在这里解释了性能方面的考虑:
https://4comprehension.com/implementing-a-randomized-stream-spliterator-in-java/
toList()
方法并没有保证返回的List
是可变的。由于当前实现的toList()
恰好返回一个可变的ArrayList
,所以在实践中是有效的。为了确保如果实现更改仍然正确,我们可以显式声明集合类型(Collectors.toCollection(ArrayList:: new)
),或者将toList
的结果复制到可变的列表中(new ArrayList<>(integers)
)。 - M. Justinget
方法。与Stream API的许多部分一样,这真是太糟糕了。按照这个论点的逻辑结论,你应该实际使用返回列表上的唯一方法是size
和iterator
。 - Paul Boddingtonsize
操作(虽然这些肯定是异常情况)--https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentLinkedQueue.html#size-- - M. Justin