Java 8中stream().map()和stream.map({})之间的区别 [英] Difference between stream().map() and stream.map({}) in java 8

查看:3415
本文介绍了Java 8中stream().map()和stream.map({})之间的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

昨天我偶然发现了一些我既不了解又找不到解释的东西:

请考虑以下操作:

Stream.of(1, 2, 3).map(i -> i * 2).forEach(System.out::println);

//This one won't compile
Stream.of(1, 2, 3).map(i -> { i * 2; }).forEach(System.out::println);

第二个似乎可以扩展到

Stream.of(1, 2, 3).map(i -> { return i * 2;}).forEach(System.out::println);

,它将正常编译.我之所以提出这个建议,是因为我已经习惯了第一个版本,但是我的IDE(Netbeans)总是指最后一个版本.

所以我的问题是: 这两种实现的区别/优势是什么?为什么带有{}块的那个需要返回值?在哪里需要该值(使代码编译除外)?

更新:

关于何时Java 8 lambda中的花括号是可选的语法?,这不能仅仅与lambda表达式语法有关,因为

Stream.of(1, 2, 3).forEach(i -> { System.out.println(i); });

可以很好地编译,因此(出于我的理解)必须考虑map()的实现.

欢呼,本

解决方案

差异如下:

lambda表达式看起来像

parameters -> expression

parameters -> { block }

其中block返回一个值-或不显示类似void的行为.

换句话说,如果expression具有非空类型,则parameters -> expression lambda等于parameters -> { return expression; };如果expression具有void类型(例如System.out.printf()),则parameters -> { expression; }等同于parameters -> { expression; }. >

您的第一个版本本质上使用带有一点开销的表达式:

i -> i = i * 2可以简化为i -> i * 2,因为i =分配是不必要的,因为i之后会立即消失,无需进一步使用.

就像

Integer function(Integer i) {
    i = i * 2;
    return i;
}

Integer function(Integer i) {
    return (i = i * 2);
}

可以简化为

Integer function(Integer i) {
    return i * 2;
}

所有这些示例都将匹配接口UnaryOperator<Integer>,这是Function<Integer, Integer>的特例.

相反,第二个例子就像

XY function(int i) {
    i = i * 2;
}

不起作用:

  • XY之一是void(这将生成与.map()不匹配的Consumer<Integer>)
  • 或者XY确实是Integer(然后缺少return语句).

在哪里需要该值(使代码可编译除外)?

好吧,.forEach(System.out::println);需要该值...

因此,所有可以转换为Function<T, R>的内容都可以赋予T流的.map(),从而产生R流:

Stream.of(1, 2, 3).map(i -> i * 2)
Stream.of(1, 2, 3).map(i -> { return i * 2; })

将您提供的Integer变成另一个Integer,从而给您另一个Stream<Integer>.您注意到它们被装箱了吗?

其他方式会

// turn a Stream<Integer> to an IntStream with a 
// int applyAsInt(Integer i) aka ToIntFunction<Integer>
Stream.of(1, 2, 3).mapToInt(i -> i * 2)
Stream.of(1, 2, 3).mapToInt(i -> { return i * 2; })

// turn an IntStream to a different IntStream with a 
// int applyAsInt(int i) aka IntUnaryOperator
IntStream.of(1, 2, 3).map(i -> i * 2)
IntStream.of(1, 2, 3).map(i -> { return i * 2; })

// turn an IntStream to a Stream<Integer> with a 
// Integer apply(int i) aka IntFunction<Integer>
IntStream.of(1, 2, 3).mapToObj(i -> i * 2)
IntStream.of(1, 2, 3).mapToObj(i -> { return i * 2; })

所有这些示例的共同点在于,它们获得一个值并产生相同或不同类型的值. (请注意,这些示例如何根据需要使用自动装箱"和自动拆箱".)

OTOH,所有可以转换为Consumer<T>的内容都可以提供给T流的.map(),该流可以是生成void表达式的任何形式的lambda:

.forEach(x -> System.out.println(x))
.forEach(x -> { System.out.println(x); }) // no return as you cannot return a void expression
.forEach(System.out::println) // shorter syntax for the same thing
.forEach(x -> { }) // just swallow the value

牢记这一点,很容易看出不能将void表达式类型的lambda赋予.map(),而不能将非void类型的lambda赋予forEach(). /p>

Yesterday i stumbled over something i neither really understand nor i'm able to find an explanation:

Consider the following operations:

Stream.of(1, 2, 3).map(i -> i * 2).forEach(System.out::println);

//This one won't compile
Stream.of(1, 2, 3).map(i -> { i * 2; }).forEach(System.out::println);

It appears that the second one can be extended to

Stream.of(1, 2, 3).map(i -> { return i * 2;}).forEach(System.out::println);

and it will just compile fine. I came up with this because i'm kinda used to the first version but my IDE (Netbeans) always refers to the last version.

So my Question is: What is the difference/advantage of these two implementations? And why does the one with the {} block requires a return value? Where is the need for that value (except to make the code compile)?

Update:

With respect to When are braces optional in Java 8 lambda syntax?, this one can't be about lambda expression syntax only because

Stream.of(1, 2, 3).forEach(i -> { System.out.println(i); });

compiles fine, so is has to be(from my understading) about the implementation of map().

Cheers, Ben

解决方案

The difference is the following:

A lambda expression looks like

parameters -> expression

or

parameters -> { block }

where either the block returns a value - or it doesn't for a void-like behaviour.

In other words, a parameters -> expression lambda is equivalent to parameters -> { return expression; } if expression has a non-void type or to parameters -> { expression; } if expression has void type (such as System.out.printf()).

Your first version essentially uses an expression with a bit of overhead:

i -> i = i * 2 could be reduced to i -> i * 2, as the i = assignment is just unnecessary, because i disappears immediately afterwards without being used any further.

It is just like

Integer function(Integer i) {
    i = i * 2;
    return i;
}

or

Integer function(Integer i) {
    return (i = i * 2);
}

which can be simplified to

Integer function(Integer i) {
    return i * 2;
}

All these examples would match the interface UnaryOperator<Integer> which is a special case for Function<Integer, Integer>.

In contrast, you 2nd example is like

XY function(int i) {
    i = i * 2;
}

which doesn't work:

  • Either XY is void (which would make a Consumer<Integer> which doesn't fit with .map())
  • Or XY is indeed Integer (then the return statement is missing).

Where is the need for that value (except to make the code compile)?

Well, .forEach(System.out::println); needs that value...

So, everything which can be converted to a Function<T, R> can be given to a .map() of a T stream, resulting in an R stream:

Stream.of(1, 2, 3).map(i -> i * 2)
Stream.of(1, 2, 3).map(i -> { return i * 2; })

turn the Integer you give into another Integer, giving you another Stream<Integer>. You notice that they are boxed?

Other ways would be

// turn a Stream<Integer> to an IntStream with a 
// int applyAsInt(Integer i) aka ToIntFunction<Integer>
Stream.of(1, 2, 3).mapToInt(i -> i * 2)
Stream.of(1, 2, 3).mapToInt(i -> { return i * 2; })

// turn an IntStream to a different IntStream with a 
// int applyAsInt(int i) aka IntUnaryOperator
IntStream.of(1, 2, 3).map(i -> i * 2)
IntStream.of(1, 2, 3).map(i -> { return i * 2; })

// turn an IntStream to a Stream<Integer> with a 
// Integer apply(int i) aka IntFunction<Integer>
IntStream.of(1, 2, 3).mapToObj(i -> i * 2)
IntStream.of(1, 2, 3).mapToObj(i -> { return i * 2; })

All these examples have in common that they get a value and produce a value of either the same or a different type. (Note how these examples use AutoBoxing and AutoUnboxing as needed.)

OTOH, everything which can be converted to a Consumer<T> can be given to a .map() of a T stream, which can be any form of lambda which produces a void expression:

.forEach(x -> System.out.println(x))
.forEach(x -> { System.out.println(x); }) // no return as you cannot return a void expression
.forEach(System.out::println) // shorter syntax for the same thing
.forEach(x -> { }) // just swallow the value

With that in mind, it is easy to see that a lambda with void expression type cannot be given to .map(), and a lambda with a non-void type cannot be given to forEach().

这篇关于Java 8中stream().map()和stream.map({})之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆