从并行流中收集部分结果

时间:2015-08-06 22:39:45

标签: parallel-processing java-8 java-stream

Java8中,处理两个并行流中的项目对,如下所示:

final List<Item> items = getItemList();
final int l = items.size();
List<String> results = Collections.synchronizedList(new ArrayList<String>());
IntStream.range(0, l - 1).parallel().forEach(
    i -> {
        Item item1 = items.get(i);
        int x1 = item1.x;
        IntStream.range(i + 1, l).parallel()
            .forEach(j -> {
                Item item2 = items.get(j);
                int x2 = item2.x;
                if (x1 + x2 < 200) return;
                // code that writes to ConcurrentHashMap defined near results
                if (x1 + x2 > 500) results.add(i + " " + j);
            });
    }
);

每个流对都写入ConcurrentHashMap,并且根据某些条件,它可以通过调用return;来终止流执行,或者它可以写入同步列表。

我想让流返回像return i + " " + j这样的结果,并将这些结果收集到外面的列表字符串中。它应该是部分的,因为必须支持返回任何内容(如果是x1 + x2 < 200)。

实现这一目标的最省时(最快的代码)方法是什么?

2 个答案:

答案 0 :(得分:0)

在这个答案中,我不会解决时间效率问题,因为事先应该处理正确性问题。

正如我在评论中所说,如果我们并行化流,则无法在某个条件之后停止流执行。否则,在触发停止条件(i,j)的对之后,可能会有一些已经执行的对x1 + x2 < 200在数字上。 另一个问题是lambda中的return;,它所做的就是跳过if所拥有的j的第二个x1 + x2 < 200,但是流将继续j+1 1}}。

没有简单的方法可以在Java中停止流,但是我们可以使用allMatch实现这一点,因为我们可以预期,一旦找到false值,它就会短路并以正确的方式返回false

因此,这将是您的代码的正确版本:

IntStream.range(0, l - 1).allMatch(i -> {
    int x1 = items.get(i).x;
    return IntStream.range(i + 1, l).allMatch(j -> {
        int x2 = items.get(j).x;
        if (x1 + x2 < 200) {
            return false;
        } else {
            if (x1 + x2 > 500) results2.add(i + " " + j);
            return true;
        }
    });
});

对于以下示例,使用构造函数Item(int x, int y)

final List<Item> items = Arrays.asList(
        new Item(200, 0),
        new Item(100, 0),
        new Item(500, 0),
        new Item(400, 0),
        new Item(1, 0));

我的版本中results的内容是:

[0 2, 0 3, 1 2]

使用您的代码(每次执行中的顺序和元素不同):

[2 4, 2 3, 1 2, 0 3, 0 2]

答案 1 :(得分:-1)

我认为这会更有效率(虽然没有做任何微观基准测试):

IntStream.range(0,l-1).forEach(
    i -> IntStream.range(i+1,l)
                  .filter(j -> items.get(i).x + items.get(j).x > 500)
                  .forEach(j -> results.add(i + " " + j)));

但是,如果我真的担心这样做的时间,我会更加注意List使用items实现的类型。甚至可能在进入lambda之前将列表转换为HashMap<Integer, Item>。例如,如果itemsLinkedList,则对lambda的任何改进都可能无关紧要,因为items.get()会一直消耗掉。