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

Java8减少BinarySwitator是用来做什么的?

叶桐
2023-03-14

我目前正在读O'reilly的《Java 8 Lambdas》是一本非常好的书。我遇到了这样一个例子。

我有一个

private final BiFunction<StringBuilder,String,StringBuilder>accumulator=
(builder,name)->{if(builder.length()>0)builder.append(",");builder.append("Mister:").append(name);return builder;};

final Stream<String>stringStream = Stream.of("John Lennon","Paul Mccartney"
,"George Harrison","Ringo Starr");
final StringBuilder reduce = stringStream
    .filter(a->a!=null)
    .reduce(new StringBuilder(),accumulator,(left,right)->left.append(right));
 System.out.println(reduce);
 System.out.println(reduce.length());

这会产生正确的输出。

先生:约翰·列侬,先生:保罗·麦卡特尼,先生:乔治·哈里森,先生:林戈·斯塔尔

我的问题是将reduce方法视为最后一个参数,即二进制运算符

我的问题是这个参数用于哪个?如果我改变

.reduce(new StringBuilder(),accumulator,(left,right)->new StringBuilder());

如果我传递NULL,则输出相同,然后返回N.P.E。

for此参数用于什么?

更新

为什么如果我在并行流上运行它,我会收到不同的结果?

第一次运行。

returned StringBuilder length = 420

第二次运行

returned StringBuilder length = 546

第三次跑步

returned StringBuilder length = 348

等等为什么。。。不应该在每次迭代时返回所有值?

任何帮助都是非常感激的。

谢谢

共有2个答案

仰翔
2023-03-14

诺西德的回答基本正确(1),但我想放大一个特定点。

reduce的标识参数必须是标识值。如果它是一个对象,这没关系,但如果是,它应该是不可变的。如果“identity”对象发生变异,它就不再是一个identity!关于这一点的更多讨论,请参阅我对一个相关问题的回答。

看起来这个例子起源于理查德·沃伯顿的例子5-19,Java8 Lambdas,O'Reilly 2014。如果是这样,我将不得不与善良的沃伯顿博士谈谈这件事。

朱皓
2023-03-14

接口流中的方法reduce重载。具有三个参数的方法的参数为:

  • 身份

组合器支持并行执行。显然,它不用于连续流。然而,没有这样的保证。如果您将流更改为并行流,我想您会看到不同:

Stream<String>stringStream = Stream.of(
    "John Lennon", "Paul Mccartney", "George Harrison", "Ringo Starr")
    .parallel();

下面是一个示例,说明如何使用组合器将顺序归约转换为支持并行执行的归约。有一个流有四个字符串,acc用作累加器的缩写。应用。然后,可以按以下方式计算减少的结果:

acc(acc(acc(acc(identity, "one"), "two"), "three"), "four");

使用兼容的组合器,可以将上述表达式转换为以下表达式。现在可以在不同的线程中执行这两个子表达式

combiner.apply(
    acc(acc(identity, "one"), "two"),
    acc(acc(identity, "three"), "four"));

关于你的第二个问题,我用一个简化的累加器来解释问题:

BiFunction<StringBuilder,String,StringBuilder> accumulator =
    (builder,name) -> builder.append(name);

根据流::reduce的Javadoc,累加器必须是关联的。在这种情况下,这意味着以下两个表达式返回相同的结果:

acc(acc(acc(identity, "one"), "two"), "three")  
acc(acc(identity, "one"), acc(acc(identity, "two"), "three"))

对于上面的累加器,情况并非如此。问题是,您正在更改标识引用的对象。这对于减少操作来说是个坏主意。这里有两个替代实现应该可以工作:

// identity = ""
BiFunction<String,String,String> accumulator = String::concat;

// identity = null
BiFunction<StringBuilder,String,StringBuilder> accumulator =
    (builder,name) -> builder == null
        ? new StringBulder(name) : builder.append(name);
 类似资料:
  • (这里的问题和我问的不一样,是问为什么我们在使用lambda表达式的同时需要函数接口,我的问题是:除了使用lambda表达式之外,函数接口还有哪些其他用途?)

  • 本文向大家介绍call和apply是用来做什么?相关面试题,主要包含被问及call和apply是用来做什么?时的应答技巧和注意事项,需要的朋友参考一下 参考回答: Call和apply的作用是一模一样的,只是传参的形式有区别而已 1、改变this的指向 2、借用别的对象的方法, 3、调用函数,因为apply,call方法会使函数立即执行

  • 我们已经看到,IPv6 可以简化一些功能,比如全球地址寻址、主机地址的自动配置等。由于 IPv6 提供的地址总数能满足我们今后几百年甚至上千年的需求,我们可以为所能想到的几乎所有物体都分配一个全球唯一的 IPv6 地址。这也是因特网规范的初衷 —— 每一个 IP 设备间都可以相互通信。这种端对端的通信允许整个因特网或者任何 IP 设备都可以双向通信,这将促进产生新的应用,新的存储、发送、接收和访问

  • 问题内容: 该参数对和有什么用?例如,给定以下代码,的作用是什么? 问题答案: 该属性指定从模型回到模型的反向关系的名称。 如果不指定,Django会自动使用带有后缀的型号的名称创建一个,例如。 如果确实在模型上指定,则仍然可以使用,但是User.maps.语法显然更简洁,更简洁。因此,例如,如果你有一个用户对象,则可以用来获取Map模型中与关联的所有实例。 该Django文档有更多的细节。

  • 问题内容: 该参数对和有什么用?例如,给定以下代码,的作用是什么? 问题答案: 该属性指定从模型回到模型的反向关系的名称。 如果不指定,Django会自动使用带有后缀的型号的名称创建一个,例如。 如果确实在模型上指定,则仍然可以使用,但是.语法显然更简洁,更简洁。因此,例如,如果你有一个用户对象,则可以用来获取Map模型中与关联的所有实例。