在并行Stream#filter中使用随机数是否有效?

时间:2017-12-11 11:28:22

标签: java random java-stream

问题是采取输入集合,随机过滤它(例如包含50%概率的所有元素,否则跳过它们)。这可以通过以下方式定期进行循环:

Random random = new Random();
List<Object> list;    // suppose that this list is populated with some elements
List<Object> filteredList = new ArrayList<>();
for (int i  = 0; i < list.size(); ++i) {
    if (random.nextDouble() < 0.5) {
        filteredList.add(list.get(i));
    }
}

当上面的代码被重写为Java 8流时,它看起来像这样:

Random random = new Random();
List<Object> list;
List<Object> filteredList = list.stream()
    .filter(element -> random.nextDouble() < 0.5)
    .collect(Collectors.toList());

我的问题是,在使用Collection#parallelStream而不是Collection#stream时,这是否是一种有效的方法。通过有效的方法,我的意思主要是这个数字真的是伪随机的 - 不会因为在多个线程中重用同一个对象而影响随机性吗?使用ThreadLocalRandom#current会有所作为吗?我的直觉是重复使用在流之外创建的相同对象可能会产生影响,而在过滤方法中创建实例

.filter(element -> ThreadLocalRandom.current().nextDouble() < 0.5)

是首选方式。或者我缺少什么重点?

编辑:如果在ThreadLocalRandom#current方法中通过filter获取随机实例是正确的方法,如果我事先将其实例化并使用相同的实例会发生什么(如第一个代码示例所示)?来自不同线程的nextDouble并发调用是否会返回相同的数字?

1 个答案:

答案 0 :(得分:2)

来自文档:

  

java.util.Random的实例是线程安全的。但是,并发   跨线程使用相同的java.util.Random实例可能会遇到   争用和随之而来的糟糕表现。考虑改为使用   多线程设计中的ThreadLocalRandom

伪随机性中断的唯一情况是当您使用相同种子在不同线程上初始化不同的随机源时(例如,当您使用当前时间作为种子,然后基于相同种子创建不同的ThreadLocalRandom时)。

编辑:至于在不同的线程中使用相同的ThreadLocalRandom,基本上,ThreadLocalRandom使用一些魔法来确保它从当前线程获取其种子(参见nextSeed()的实现) 。