Java 8中stream().map()和stream.map({})之间的区别 [英] Difference between stream().map() and stream.map({}) in java 8
问题描述
昨天我偶然发现了一些我既不了解又找不到解释的东西:
请考虑以下操作:
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
isvoid
(which would make aConsumer<Integer>
which doesn't fit with.map()
) - Or
XY
is indeedInteger
(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屋!