Java 8在并行流中更新映射

Java 8在并行流中更新映射,java,concurrency,java-8,concurrenthashmap,Java,Concurrency,Java 8,Concurrenthashmap,我有两个循环。在内部循环中,我点击一个数据库,得到结果并对结果执行一些计算(包括调用其他私有方法),然后将结果放入映射中。 这种方法会不会导致任何问题,比如为任何键设置null 没有两个线程将更新相同的值。i、 e)计算的键将是唯一的。(如果循环n次,则会有n键) Map m=新的ConcurrentHashMap(); obj1.getProp().parallelStream().forEach(k1->{//obj.getProp()返回一个列表 obj2.parallelStream()

我有两个循环。在内部循环中,我点击一个数据库,得到结果并对结果执行一些计算(包括调用其他私有方法),然后将结果放入映射中。 这种方法会不会导致任何问题,比如为任何键设置
null

没有两个线程将更新相同的值。i、 e)计算的
键将是唯一的。(如果循环
n
次,则会有
n
键)

Map m=新的ConcurrentHashMap();
obj1.getProp().parallelStream().forEach(k1->{//obj.getProp()返回一个列表
obj2.parallelStream().forEach(k2->{//obj2是一个列表
字符串键=构造键(k1,k2);
//点击一个DB并得到结果
//对结果的计算
//调用其他方法
m、 放置(键、结果);
});
});

您的问题可以归结为“我可以从多个线程添加到并发哈希映射吗?”和“我可以并行访问我的数据库吗?”

第一个答案是“是”,第二个答案是“视情况而定”

或者再长一点:您使用的两个并行流基本上只是在执行池中的多个线程上启动内部lambda。添加到映射本身不是问题,这就是并发哈希映射的目的


关于数据库,这取决于您查询它的方式以及共享对象的级别。如果对每个线程使用具有不同连接的连接池,则可能不会有问题。对于大多数数据库,共享一个连接并为每个线程获取一个新语句也很好。共享语句和获取新结果集会给许多数据库驱动程序带来问题。

除非您完全理解流API不仅仅是循环的另一种拼写方法,否则不应使用流API。通常,如果您的代码包含流上的
forEach
,您应该至少问自己一次这是否是任务的最佳解决方案,但是如果您的代码包含嵌套的
forEach
调用,您应该知道这不可能是正确的

它可能会工作,就像在您的问题中添加到并发映射时一样,但是,它违背了流API的目的

除此之外,数组没有
parallelStream()
方法,因此,当
obj.getProp()
的结果类型和
obj2
的类型是数组时,正如您的注释所说,您必须使用
arrays.stream(…)
来构造流

您想要做的事情可以实现为

Map<String,String> m = 
    Arrays.stream(obj1.getProp()).parallel()
        .flatMap(k1 -> Arrays.stream(obj2).map(k2 -> constructKey(k1, k2)))
        .collect(Collectors.toConcurrentMap(key -> key, key -> {
            //Hit a DB and get the result
            //Computations on the result
            //Call some other methods
            return result;
        }));
Map m=
Arrays.stream(obj1.getProp()).parallel()
.flatMap(k1->array.stream(obj2).map(k2->constructKey(k1,k2)))
.collect(收集器.toConcurrentMap(键->键,键->{
//点击一个DB并得到结果
//对结果的计算
//调用其他方法
返回结果;
}));
这样做的好处不仅是更好地利用了并行处理,而且即使您使用
收集器.toMap
,而不是
收集器,创建一个非并发
映射
,它也能工作;框架将负责以线程安全的方式生成它

因此,除非您确实需要一个并发映射来进行后续的并发处理,否则您可以使用:;哪一个会表现得更好取决于讨论超出本答案范围的因素


因此,只要正确使用流API,它将是线程安全的,无论您生成的是哪种
Map
类型,剩下的问题是数据库访问是否是线程安全的,这取决于您的问题中没有包含的许多因素,因此我们无法回答。

我的道歉。这不是一个数组,而是一个列表。根据你的回答,我可以将Arrays.stream与list.parallelStream进行转换。是的,无论你是使用
Arrays.stream(array.parallel()
还是
list.parallelStream()
,流操作的逻辑都是相同的。整个流操作将在并行模式下运行,因此,对于内部流使用
list.stream()
还是
list.parallelStream()
,都无关紧要。我不明白你的最后一点。如何说对内部流使用
stream()
parallelStream()
没有任何区别?这是一个单流操作,尽管传递给
flatMap
的函数返回新的流实例。因此,术语“内流”和“外流”只是口语。“外部流”的
flatMap
实现决定了如何处理“内部流”的元素,因此“内部流”标记为并行还是顺序是无关的。
Map<String,String> m = 
    Arrays.stream(obj1.getProp()).parallel()
        .flatMap(k1 -> Arrays.stream(obj2).map(k2 -> constructKey(k1, k2)))
        .collect(Collectors.toConcurrentMap(key -> key, key -> {
            //Hit a DB and get the result
            //Computations on the result
            //Call some other methods
            return result;
        }));