Java并行流填充数组

时间:2018-12-24 13:41:08

标签: java java-stream java-10

我有一个很大的哈希图,里面充满了质数。

var mapA = new HashMap<Integer, Long>();

我需要对其进行大量计算,因此我在使用并行流:

var res = new ArrayList<Integer();

mapA.entrySet()
        .parallelStream()
        .forEach( x -> {

            var values = mapA.entrySet()
                                    .parallelStream()
                                    .filter( /*conditions*/ )
                                    .map(y -> y.getKey())
                                    .toArray();                 

            Arrays.stream(values)
                      .parallel()
                      .sorted()
                      .forEach(val -> {

                           synchronized (this) {
                                res.add(x.getKey());
                                res.add((Integer) val);
                           }

                      });


        });

如您所见,存在res,它是一个不在流范围内的数组。我需要循环是并行的,否则计算可能要花费几分钟和几分钟。这需要吗?

.forEach(val -> {

    synchronized (this) {
        res.add(x.getKey());
        res.add((Integer) val);
    }

});

我之所以添加synchronized是因为由于流是并行运行的,所以我不希望出现竞争条件,以防两个或更多线程同时在res中添加数据。

我尝试删除同步的(this),程序仍然可以正常工作。但是我如何确定它始终可以正常工作?

谢谢


如果需要,我将在此处添加整个代码:

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class DiffieHellman {

    private static final int LIMIT = 65536;

    private final long p;
    private final long g;

    public DiffieHellman(long p, long g) {
        this.p = p;
        this.g = g;
    }

    public List<Integer> tryBruteForce(long publicA, long publicB) {
        List<Integer> res = new ArrayList<Integer>();

        var mapA = new HashMap<Integer, Long>(
                IntStream
                        .rangeClosed(0, LIMIT)
                        .parallel()
                        .boxed()
                        .collect(
                                Collectors.toMap(x -> x, x -> DiffieHellmanUtils.modPow(publicB, x, p))
                        )
        );

        var mapB = new HashMap<Integer, Long>(
                IntStream
                        .rangeClosed(0, LIMIT)
                        .parallel()
                        .boxed()
                        .collect(
                                Collectors.toMap(x -> x, x -> DiffieHellmanUtils.modPow(publicB, x, p))
                        )
        );

        mapA.entrySet()
                    .parallelStream()
                    .forEach( x -> {

                        var values = mapB.entrySet()
                                        .parallelStream()
                                        .filter( y -> y.getValue().equals(x.getValue()))
                                        .map(Map.Entry::getKey)
                                        .toArray(Integer[]::new);

                        Arrays.stream(values)
                                .parallel()
                                .sorted()
                                .forEach(val -> {
                                        res.add(x.getKey());
                                        res.add((Integer) val);
                                });


                    });

        return res;
    }

}

1 个答案:

答案 0 :(得分:1)

自然地,您可以像其他答案所指出的那样简单地使用同步集合,但是由于争用,该集合的性能可能不足,并且编写起来仍然很麻烦。

相反,可以通过惯用Stream API以稍微不同的方式解决问题。

首先,嵌套操作可以在单个流管道中完成:

mapB.entrySet()
            .parallelStream()
            .filter(y -> y.getValue().equals(x.getValue()))
            .map(y -> y.getKey())
            .sorted()
            .forEach(val -> {

                synchronized (this) {
                    res.add(x.getKey());
                    res.add((Integer) val);
                }
            });

第二,为了避免并发问题,最简单的方法是放弃命令式方法并利用Stream API的声明性。

为此,我们不会手动for-each然后将add个元素添加到结果中,而是让Stream进行管理。

您要在此处创建一个新序列,方法是将mapA entrySet()的每个元素替换为自定义序列:

List<Integer> res = mapA.entrySet()
      .parallelStream()
      .flatMap(x -> mapB.entrySet().stream()
         .filter(y -> y.getValue().equals(x.getValue()))
         .map(Map.Entry::getKey)
         .sorted()
         .flatMap(v -> Stream.of(x.getKey(), v)))
      .collect(Collectors.toList());

嵌套parallelStream可以省略,因为无论如何flatMap在流上调用sequential()