有没有办法并行化这段代码:
HashMap<String, Car> cars;
List<Car> snapshotCars = new ArrayList<>();
...
for (final Car car : cars.values()) {
if (car.isTimeInTimeline(curTime)) {
car.updateCalculatedPosition(curTime);
snapshotCars.add(car);
}
}
更新:这是我在寻求帮助之前尝试过的:
snapshotCars.addAll(cars.values().parallelStream()
.filter(c -> c.isTimeInTimeline(curTime))
.collect(Collectors.toList()));
我怎样才能整合这条线? - &GT; car.updateCalculatedPosition(CURTIME);
答案 0 :(得分:1)
好吧,假设updateCalculatedPosition
不会影响运行它的Car
对象之外的状态,那么使用peek
可能足够安全:
List<Car> snapshotCars = cars.values()
.parallelStream()
.filter(c -> c.isTimeInTimeline(curTime))
.peek(c -> c.updateCalculatedPosition(curTime))
.collect(Collectors.toCollection(ArrayList::new));
我说这是足够安全&#34;因为collect
指示peek
将要查看哪些元素,这些元素必然是通过过滤器的所有项目。但是,请阅读this answer,了解为什么peek
通常应该避免重要&#34;操作
您的无偷窥替代方案是首先,过滤和收集,然后使用完成的集合进行更新:
List<Car> snapshotCars = cars.values()
.parallelStream()
.filter(c -> c.isTimeInTimeline(curTime))
.collect(Collectors.toCollection(ArrayList::new));
snapShotCars.parallelStream()
.forEach(c -> c.updateCalculatedPosition(curTime));
从API的角度来看,这样更安全,但并行性更低 - 只有在完成过滤和收集后才开始更新位置。
答案 1 :(得分:0)
如果您希望对列表进行并行访问,则可能需要使用Collections.synchonizedList
来获取线程安全列表:
List<Car> snapshotCars = Collections.synchronizedList(new ArrayList<>());
然后您可以像这样使用流API:
cars.values()
.parallelStream()
.filter(car -> car.isTimeInTimeline(curTime))
.forEach(car -> {
car.updateCalculatedPosition(curTime);
snapshotCars.add(car);
});
答案 2 :(得分:0)
除RealSkeptic’s answer外,您还可以使用自己的收藏家:
List<Car> snapshotCars = cars.values().parallelStream()
.filter(c -> c.isTimeInTimeline(curTime))
.collect(ArrayList::new,
(l,c) -> { c.updateCalculatedPosition(curTime); l.add(c); },
List::addAll);
请注意,.collect(Collectors.toList())
与.collect(Collectors.toCollection(ArrayList::new))
相同(但不一定相同),相当于.collect(ArrayList::new, List::add, List::addAll)
。
所以我们的自定义收集器执行类似的操作,但用一个函数替换累加器,该函数也执行所需的附加操作。