当前位置: 首页 > 面试题库 >

如何将Java8流的元素添加到现有列表中

潘佐
2023-03-14
问题内容

Collector的Javadoc显示了如何将流的元素收集到新的List中。有没有一种将结果添加到现有ArrayList中的方法?


问题答案:

注意: osid的答案
显示了如何使用来添加到现有集合forEachOrdered()。这是对现有集合进行变异的有用且有效的技术。我的答案解决了为什么您不应该使用A
Collector来突变现有集合的原因。

简短的答案是 no ,至少在一般情况下不是这样,您不应该使用a Collector修改现有集合。

原因是收集器被设计为支持并行性,即使是在不是线程安全的收集器上也是如此。他们这样做的方法是让每个线程根据自己的中间结果集合独立运行。每个线程获取其自己的集合的方式是调用每次Collector.supplier()返回一个
集合所需的。

然后,再次以线程受限的方式合并这些中间结果的集合,直到只有一个结果集合为止。这是操作的最终结果collect()

来自[Balder一些答案建议使用Collectors.toCollection()然后传递一个返回现有列表而不是新列表的供应商。这违反了供应商的要求,即每次都返回一个新的空集合。

如其答案中的示例所示,这将适用于简单的情况。但是,它将失败,特别是如果流并行运行。(该库的未来版本可能会以某种无法预料的方式更改,即使在顺序的情况下也会导致其失败。)

让我们举一个简单的例子:

List<String> destList = new ArrayList<>(Arrays.asList("foo"));
List<String> newList = Arrays.asList("0", "1", "2", "3", "4", "5");
newList.parallelStream()
       .collect(Collectors.toCollection(() -> destList));
System.out.println(destList);

当我运行该程序时,通常会收到一个ArrayIndexOutOfBoundsException。这是因为多个线程正在对ArrayList一个线程不安全的数据结构进行操作。好的,让我们使其同步:

List<String> destList =
    Collections.synchronizedList(new ArrayList<>(Arrays.asList("foo")));

这将不会再因异常而失败。但是,而不是预期的结果:

[foo, 0, 1, 2, 3]

它给出了如下奇怪的结果:

[foo, 2, 3, foo, 2, 3, 1, 0, foo, 2, 3, foo, 2, 3, 1, 0, foo, 2, 3, foo, 2, 3, 1, 0, foo, 2, 3, foo, 2, 3, 1, 0]

这是我上面描述的线程限制的累积/合并操作的结果。在并行流的情况下,每个线程都调用供应商以获取自己的集合以进行中间累积。如果传递的供应商返回 相同的
集合,则每个线程会将其结果附加到该集合。由于线程之间没有顺序,结果将以任意顺序附加。

然后,当这些中间集合被合并时,这基本上将列表与其自身合并。使用合并列表List.addAll(),表示如果在操作过程中修改了源集合,则结果是不确定的。在这种情况下,请ArrayList.addAll()执行阵列复制操作,因此最终会自我复制,这大概是我期望的。(请注意,其他List实现可能具有完全不同的行为。)无论如何,这解释了奇怪的结果和目标中重复的元素。

您可能会说:“我将确保按顺序运行流”,然后继续编写这样的代码

stream.collect(Collectors.toCollection(() -> existingList))

无论如何。我建议不要这样做。当然,如果您控制流,则可以保证它不会并行运行。我希望会出现一种编程风格,即流传递而不是集合传递。如果有人将流交给您,并且您使用此代码,则如果流碰巧是并行的,它将失败。更糟糕的是,有人可能会递给您一个顺序流,并且此代码将在一段时间内正常工作,通过所有测试等。然后,在任意时间后,系统中其他地方的代码可能会更改为使用并行流,这将导致
您的 代码打破。

确定,然后确保sequential()在使用此代码之前记得记得在任何流上调用:

stream.sequential().collect(Collectors.toCollection(() -> existingList))

当然,您会记得每次都这样做,对吗?:-)假设您愿意。然后,性能团队会想知道为什么他们所有精心设计的并行实现都没有提供任何加速。然后,他们再次将其追溯到
您的 代码,这迫使整个流按顺序运行。

不要这样



 类似资料:
  • Collector的Javadoc展示了如何将流的元素收集到一个新列表中。是否有一个单行程序将结果添加到现有的ArrayList中?

  • 我必须编写一些代码,将Java8流的内容多次添加到列表中,我很难找到最好的方法。根据我在SO上读到的内容(主要是这个问题:如何将Java8流的元素添加到现有列表中)和其他地方,我将其缩小到以下选项: PS:在我的示例代码中,我使用作为需要在流上执行的任何操作的占位符

  • 我是新来的laravel框架。为了使博客网址对搜索引擎优化友好,我需要为laravel网站的现有博客表添加一个额外的列。我们可以直接在数据库中的表中添加列吗?我们可以在没有命令或迁移的情况下添加列吗?你能建议一个简单的方法来添加这个列吗?

  • 问题内容: 我的问题是我想在遍历新元素的同时扩展一个包含新元素的列表,并且希望迭代器继续刚才添加的元素。 根据我的理解,会在列表中的当前元素之前而不是之后添加一个元素。是否可以通过其他方式实现这一目标? 问题答案: 除了以外,您无法在修改集合时使用进行迭代。 但是,如果使用方法,该方法返回,并对其进行迭代,则您还有更多要修改的选项。从javadoc中获取: 新元素插入到隐式光标之前:…后续调用将返

  • 问题内容: 如何添加到MySQL表的现有列? 问题答案: 我认为您想按照命令中的说明进行操作。可能是这样的: 在上面运行之前,请确保该列具有主索引。

  • 问题内容: 我想避免。我该怎么办? 问题答案: 您可以在迭代本身期间使用支持remove / add方法的。