在尝试扩展您的代码之前,您应该先了解几个基本错误。
首先,forEach
不能保证元素处理的特定顺序,因此即使是对于顺序流,它也很可能不是添加到List
的正确工具。但是,如果使用并行流将其用于添加到诸如LinkedList
之类的非线程安全集合,则完全是错误的,因为此操作将被同时执行。
但是,即使resourceMemory
是线程安全的集合,您的代码仍然有问题,因为filter
条件与终端操作之间存在干扰。 .filter(o -> !resourceMemory.contains(o))
查询您正在修改的相同列表,并且不难理解即使是使用线程安全集合也会出现问题:
两个或更多线程可能会处理过滤器并发现元素未包含在列表中,然后所有这些线程都会添加该元素,这与您明显的无重复意图相矛盾。
您可以使用forEachOrdered
,它将按顺序且非并发地执行操作:
body.getSurroundings().parallelStream()
.filter(o -> o instanceof ResourcePoint)
.map(o -> (ResourcePoint)o)
.forEachOrdered(o -> {// not recommended, just for explanation
if(!resourceMemory.contains(o))
resourceMemory.add(o);
});
这个方法是可行的,你可以在这个操作中添加到另一个列表中,但这远不是推荐的编码风格。此外,这个终端操作会与所有处理线程同步,这将破坏任何并行处理的潜在好处,特别是当流管道中最耗时的操作是在 LinkedList 上调用 contains 方法时,它只能单线程运行。
正确的收集流元素到列表中的方法是使用 collect 方法,正如其名称所示:
List<ResourcePoint> resourceMemory
=body.getSurroundings().parallelStream()
.filter(o -> o instanceof ResourcePoint)
.map(o -> (ResourcePoint)o)
.distinct() // no duplicates
.collect(Collectors.toList()); // collect into a list
这并不返回一个
LinkedList
,但你应该仔细重新考虑是否真的需要一个
LinkedList
。在 99% 的情况下,你并不需要它。如果你确实需要一个
LinkedList
,你可以用
Collectors.toCollection(LinkedList::new)
替换
Collectors.toList()
。
现在,如果你真的必须添加到一个已经包含元素的、由你无法控制的现有列表中,你应该考虑上面提到的事实,即你必须确保对非线程安全列表的单线程访问,因此从并行流中进行这样的操作没有任何好处。在大多数情况下,更有效的做法是让流独立于该列表工作,并在单线程步骤中添加结果。
Set<ResourcePoint> newElements=
body.getSurroundings().parallelStream()
.filter(o -> o instanceof ResourcePoint)
.map(o -> (ResourcePoint)o)
.collect(Collectors.toCollection(LinkedHashSet::new))
newElements.removeAll(resourceMemory)
resourceMemory.addAll(newElements)
在这里,我们使用
LinkedHashSet
来收集元素,这意味着维护遇到的顺序并排序出新元素中的重复项,然后在新元素上使用
removeAll
来删除目标列表中现有的元素(这里我们从临时集合的哈希集特性中受益),最后将新元素添加到目标列表中。正如解释的那样,对于一个非线程安全的目标集合,这必须是单线程完成的。
使用此解决方案将newElements
添加到另一个目标集合很容易,比编写自定义收集器在流处理期间生成两个列表要简单得多。但请注意,上述流操作太便宜了,无法假设从并行处理中获得任何好处。您需要非常大量的元素才能弥补初始的多线程开销。甚至可能不存在某个数字,使其值得付出。
peek()
或自定义收集器。 - Peter Lawreyo instanceof ResourcePoint
还是o.getClass() == ResourcePoint.class
?决定一个,但不要使用o.getClass().equals(ResourcePoint.class)
,那会混淆实际意图。此外,当使用并行流时,你的代码存在多个问题。请仔细阅读 https://docs.oracle.com/javase/8/docs/api/?java/util/stream/package-summary.html。 - Holger.forEach(o -> { resourceMemory.add(o); myOtherList.add(o); })
。但请注意Holger的评论。为了修复这段代码,您需要更多地说明您真正想要实现什么。 - Tunaki