当前位置: 首页 > 知识库问答 >
问题:

将流中的对象同时添加到两个不同的列表中

方通
2023-03-14

如何将一个流中的对象同时添加到两个不同的列表中

目前我正在做

body.getSurroundings().parallelStream()
                .filter(o -> o.getClass().equals(ResourcePoint.class))
                .map(o -> (ResourcePoint)o)
                .filter(o -> !resourceMemory.contains(o))
                .forEach(resourceMemory::add);

要将流中的对象添加到linkedlist“resourceMemory”中,我还想同时将相同的对象添加到另一个列表中,但我找不到它的语法。是否可能,或者每个列表需要两份代码副本?

共有2个答案

闻修筠
2023-03-14

而不是

.forEach(resourceMemory::add)

你可以调用

.forEach(o -> {
   resourceMemory.add(o);
   otherResource.add(o);
 })

或者将add操作放在一个单独的方法中,以便提供方法引用

.forEach(this::add)

void add(ResourcePoint p) {
   resourceMemory.add(o);
   otherResource.add(o);
}

但请记住,在使用并行流时,每次运行的插入顺序可能不同。

燕昊东
2023-03-14

在尝试扩展代码之前,您应该首先了解几个基本错误。

首先,foreach不保证元素处理的特定顺序,因此它可能是添加到List的错误工具,即使对于顺序流也是如此,但是,使用并行流添加到LinkedList这样的集合是完全错误的,这不是线程安全的,因为操作将同时执行。

但是,即使resourceMemory是线程安全集合,您的代码仍然被破坏,因为您的filter条件和终端操作之间存在干扰。. filter(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上调用包含时,这将(必须)发生在单线程中。

将流元素收集到列表中的正确方法是,顾名思义,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

这不会返回链接列表,但您应该仔细考虑是否真的需要链接列表。99%的情况下,你不需要。如果你真的需要链接列表,你可以更换收集器。toList()带有收集器。toCollection(LinkedList::new)

现在,如果您真的必须添加到在您的控制之外创建的现有列表中,该列表可能已经包含元素,那么您应该考虑上面提到的事实,即您必须确保单线程访问非线程安全的列表,因此在并行流中这样做没有任何好处。在大多数情况下,让流独立于该列表工作并在之后的单线程步骤中添加结果更有效:

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,这意味着维护相遇顺序并整理新元素中的重复项,然后在新元素上使用拿下所有来删除目标列表的现有元素(在这里我们受益于临时集合的哈希集性质),最后,新元素被添加到目标列表中,正如所解释的,对于不是线程安全的目标集合,无论如何都必须发生单线程。

使用此解决方案可以很容易地将newElements添加到另一个目标集合中,这比编写自定义收集器以在流处理期间生成两个列表要容易得多。但是请注意,上面写的流操作太便宜了,无法从并行处理中获得任何好处。您将需要大量元素来补偿初始的多线程开销。甚至可能没有任何数字可以得到回报。

 类似资料:
  • 在其中声明列表后 并在类PlayListStuff中添加字段

  • 如何以另一种方式从流中添加两个不同列表中的对象? 这里有一个例子

  • 以下是我目前的尝试: 我一直在思考如何动态地向列(mpg、cyl、disp)添加(1,2,3)。提前谢了。

  • 我有两个相同类型“MyInfoObject”的列表(例如A和B),这样: 我想创建这两个列表的映射,以便列表a的所有ID和具有相同签名的列表B的所有ID创建一个类型为“bucketoFaandB”的桶: 因此,我的输出将是,其中key是签名 我的建议是: 在这种情况下,我的输出将是: 我想用Java 8的流来做这件事。 我想出的一个办法是: 从创建,say abuckets 迭代bList并创建依

  • 我还将列表一中的和成员的每个哈希代码与列表二中成员的哈希代码进行了比较。而且没有区别。但是如果我比较完整列表的hashcode,就有区别了。我不知道为什么。我很无助。 也许有人能帮我。请提前向我致以最诚挚的问候和感谢。

  • 问题内容: 我有一张桌子和一张桌子。 该表具有以下结构: 所有用户( 投诉者 和 投诉解决者) 都位于table中。 如何编写查询以显示两列的用户名? 这给了我一个: 但我不知道如何编写,因此两列均显示用户名而不是ID。 问题答案: