收集器的组合器功能可以用于顺序流吗?

2023-11-27

示例程序:

public final class CollectorTest
{
    private CollectorTest()
    {
    }

    private static <T> BinaryOperator<T> nope()
    {
        return (t, u) -> { throw new UnsupportedOperationException("nope"); };
    }

    public static void main(final String... args)
    {
        final Collector<Integer, ?, List<Integer>> c
            = Collector.of(ArrayList::new, List::add, nope());

        IntStream.range(0, 10_000_000).boxed().collect(c);
    }
}

因此,为了简化这里的问题,没有最终的转换,因此生成的代码非常简单。

Now, IntStream.range()产生一个顺序流。我只是将结果装箱Integers然后是我做作的Collector将它们收集到一个List<Integer>。很简单。

无论我运行这个示例程序多少次,UnsupportedOperationException永远不会命中,这意味着我的虚拟组合器永远不会被调用。

我有点预料到了这一点,但后来我已经对流有足够的误解,所以我不得不问这个问题......

Can a Collector当流被调用时, 的组合器会被调用保证是连续的?


仔细阅读中的streams实现代码减少操作.java表明只有当ReduceTask完成,并且ReduceTask仅在并行评估管道时才使用实例。因此,在当前的实施中,在评估顺序管道时,永远不会调用组合器。

然而,规范中没有任何内容可以保证这一点。 ACollector是一个对其实现提出要求的接口,并且对于顺序流没有任何豁免。就我个人而言,我发现很难想象为什么顺序管道评估可能需要调用组合器,但是比我更有想象力的人可能会找到它的巧妙用途,并实现它。规范允许这样做,即使今天的实现没有这样做,您仍然必须考虑它。

这应该不足为奇。 Streams API 的设计中心是支持与顺序执行同等的并行执行。当然,程序可以观察它是顺序执行还是并行执行。但 API 的设计是为了支持允许两者之一的编程风格。

如果您正在编写收集器,并且发现不可能(或不方便或困难)编写关联组合器函数,导致您想要将流限制为顺序执行,也许这意味着您正走向错误的方向。是时候退一步思考以不同的方式解决问题了。

不需要关联组合器函数的常见归约式操作称为向左折叠。主要特点是折叠功能严格从左到右应用,一次进行一个。我不知道并行左折叠的方法。

当人们试图以我们所说的方式扭曲收藏家时,他们通常会寻找类似左折叠的东西。 Streams API 没有对此操作的直接 API 支持,但编写起来非常容易。例如,假设您要使用此操作减少字符串列表:重复第一个字符串,然后附加第二个字符串。很容易证明这个操作不是关联的:

List<String> list = Arrays.asList("a", "b", "c", "d", "e");

System.out.println(list.stream()
    .collect(StringBuilder::new,
             (a, b) -> a.append(a.toString()).append(b),
             (a, b) -> a.append(a.toString()).append(b))); // BROKEN -- NOT ASSOCIATIVE

按顺序运行,这会产生所需的输出:

aabaabcaabaabcdaabaabcaabaabcde

但是当并行运行时,它可能会产生如下结果:

aabaabccdde

由于它是按顺序“工作”的,因此我们可以通过调用来强制执行此操作sequential()并通过让组合器抛出异常来支持这一点。此外,供应商必须只被调用一次。无法合并中间结果,因此如果供应商被调用两次,我们就已经遇到麻烦了。但由于我们“知道”供应商在顺序模式下仅被调用一次,因此大多数人并不担心这一点。事实上,我见过人们编写“供应商”来返回一些现有对象,而不是创建一个新对象,这违反了供应商合同。

在此使用 3-arg 形式collect(),三个函数中有两个违反了合约。这难道不应该告诉我们以不同的方式做事吗?

这里的主要工作是由累加器函数完成的。为了完成折叠式缩减,我们可以使用严格的从左到右的顺序应用此函数forEachOrdered()。我们必须在前后进行一些设置和完成代码,但这没有问题:

StringBuilder a = new StringBuilder();
list.parallelStream()
    .forEachOrdered(b -> a.append(a.toString()).append(b));
System.out.println(a.toString());

当然,这在并行中工作得很好,尽管并行运行的性能优势可能会因以下的排序要求而被抵消:forEachOrdered().

总之,如果您发现自己想要进行可变归约,但缺少关联组合器函数,导致您将流限制为顺序执行,请将问题重新定义为向左折叠操作与使用forEachRemaining()在你的累加器函数上。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

收集器的组合器功能可以用于顺序流吗? 的相关文章

随机推荐

  • sheet.deleteRows() - 需要从顶部删除所有包含数据的行(最好从第二行开始)

    我需要删除 200 300 行 不包括第 1 行 下面的脚本出现越界错误 而且速度很慢 有人可以帮助提供加快该过程的方法吗 如果可能 排除 ROW 1 function clearRange var sheet SpreadsheetApp
  • 是否可以使用带有额外构建步骤的 Go 构建?

    当 go build 不够并且需要在 go build 的同时运行额外的命令时该怎么办 go tools 是否涵盖了这个用例 如果是这样 约定是什么 我注意到可以传递额外的标志来构建工具 cgo pkg config glib 2 0 go
  • Ajax 无法在 android webview 中工作

    我正在 webview 中加载一个网站 我们在网站中使用了 Ajax 它在网络浏览器和移动浏览器上也工作正常 但在 android webview 中 ajax 不起作用 控制台中没有错误 这是我的代码 public class Activ
  • F#如何扩展泛型数组类型? [复制]

    这个问题在这里已经有答案了 下列的这个问题我想知道如何 或是否 可以扩展通用 F 数组类型 我可以这样做 type System Array with member a Last a GetValue a Length 1 但正如托马斯提到
  • '#' 在 C 中到底做什么? [复制]

    这个问题在这里已经有答案了 我有一个程序 include
  • Automapper - 将多对多关联映射到平面对象的最佳实践

    我有两个实体 Employee and Team 我想要的是一个EmployeeForm具有Name of the Team 我怎样才能实现这个使用自动映射器 我当前的 解决方案 如下 Mapper CreateMap
  • 如果我不知道直到偏移量,我可以从 kafka 主题创建 RDD 吗?

    KafkaUtils createRDD将 offsetRanges 作为参数 我不知道我想读取的主题的直到偏移量 我最多想阅读该主题中的前 30 条消息 我看到有一个KafkaCluster html getLatestLeaderOff
  • 如何在 x86 平台上使用 gcc 声明内存范围不可缓存?

    虽然我已经阅读了有关此的 movntdqa 指令 但已经找到了一种干净的方法来表示不可缓存的内存范围或读取数据 以免污染缓存 我想从海湾合作委员会做到这一点 我的主要目标是交换到大型数组中的随机位置 由于数据恢复很少 希望通过避免缓存来加速
  • 如何在传递给 DataTable Select() 的字符串中包含撇号?

    The dt Select string 给出错误missing operand when apostrophe符号在字符串中 有什么方法可以解决这个错误 或者如何处理这个错误 String value Rubens Home value
  • 参考 ASP.NET Core MVC Web 应用程序 (RC2) 的完整框架库项目?

    我目前正在使用 ASP NET Core MVC RC2 它应该支持从 Core MVC Web 应用程序项目引用完整的框架库项目 但我似乎无法让它发挥作用 我有一个现有的完整框架库项目 我想将其与新的 Core MVC Web 应用程序项
  • Grails 日期验证的最小约束

    我是 grails 和 groovy 的新手 我有一个带有开始和结束日期的项目域类 我想添加一个约束 指定结束日期需要大于开始日期 然后项目的另一个子对象需要使其开始日期和结束日期与父项目的日期进行验证 这是否可以通过 min 约束实现 或
  • 我的 lambda 参数真的会影响我的本地参数吗?

    我正在处理一些 C 代码 它获取一些数据 并将其转发到传入的函数 void foo int data void fun int fun data 以下内容在没有警告的情况下工作 void bar int data int main int
  • 有没有办法用history.js 来判断状态的走向?

    正如标题所说 我希望能够表演出不一样的作品onstatechange事件如果pushState函数被调用 而不是back功能 或者 如果go函数为负函数或正函数 Example if History pushState or History
  • 如何更改Servlet 3.0 Spring MVC分段上传表单的字符编码?

    我有一个非常简单的 JSP Servlet 3 0 Spring MVC 3 1 应用程序 在我的其中一个页面上 我有多种表格 其中一种表单允许用户上传文件 因此配置为enctype multipart form data 我在 web x
  • 罗盘校准objective-c

    我尝试在我的 ios 应用程序中使用指南针 我有一个问题 如果我实施locationManagerShouldDisplayHeadingCalibration方法和return YES 然后校准显示一直显示 但我应该让它像苹果地图一样 I
  • 注释中的通用类型

    考虑以下代码 import java lang annotation Retention import java lang annotation RetentionPolicy public class AnnotationTest Gen
  • Azure 应用程序网关的自定义域

    我正在尝试使用自定义域名创建应用程序网关 但不断收到错误消息 无法指定域名标签 我想知道我是否做错了什么或者天蓝色应用程序网关不可能拥有自定义域名 以下是我在天蓝色应用程序网关方面学到的经验教训 1 Application gateway
  • Firebase Android 离线性能

    当单个节点下存储大约 5000 个子节点时 在使用离线功能时初始化 firebase 会变得非常慢 执行第一个查询大约需要 30 秒 初始化后 执行后续查询 例如列出前 25 个子节点 只需不到一秒的时间 我正在利用以下属性来启用离线功能
  • Grails 中的多个文件上传

    我正在努力整合优秀的来自 blueimp 的 jquery 文件上传进入 Grails 2 0 并为此编写一个新插件 我知道有已经是插件了 但它不使用资源 并且已经9个月没有更新了 jquery file upload在那段时间发生了很大的
  • 收集器的组合器功能可以用于顺序流吗?

    示例程序 public final class CollectorTest private CollectorTest private static