2015-04-17 30 views
7

Hôm qua tôi vấp phải thứ gì đó không thực sự hiểu hoặc tôi không thể tìm thấy giải thích:Sự khác biệt giữa luồng(). Map() và stream.map ({}) trong java 8

Hãy xem xét các thao tác sau :

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); 

dường như cái thứ hai có thể được mở rộng để

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

và nó sẽ chỉ biên dịch tốt. Tôi đã đưa ra điều này bởi vì tôi kinda được sử dụng để phiên bản đầu tiên nhưng IDE của tôi (Netbeans) luôn luôn đề cập đến phiên bản cuối cùng.

Câu hỏi của tôi là: Điều gì là điểm khác biệt/lợi thế của hai triển khai này? Và tại sao người có khối {} yêu cầu giá trị trả lại? Nhu cầu về giá trị đó ở đâu (ngoại trừ việc biên dịch mã)?

Cập nhật:

Đối với When are braces optional in Java 8 lambda syntax?, chương trình này không thể về cú pháp của biểu thức lambda chỉ vì

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

biên dịch tốt, như vậy là có được (từ understading của tôi) về việc triển khai map().

Chúc mừng, Bến

+1

Phiên bản "không cần thiết" yêu cầu phía bên tay phải của dấu mũi tên là biểu thức Java hợp lệ (lưu ý: biểu thức, không phải câu lệnh!); đây là lý do tại sao nó hoạt động. Nhưng cuối cùng cả hai làm điều tương tự. – fge

Trả lời

9

Sự khác biệt là như sau:

Một biểu thức lambda trông giống như

parameters -> expression 

hoặc

parameters -> { block } 

nơi một trong hai block trả về một giá trị - hoặc nó không cho hành vi giống như void.

Nói cách khác, một lambda parameters -> expression tương đương với parameters -> { return expression; } nếu expression có một loại phi khoảng trống hoặc để parameters -> { expression; } nếu expression có kiểu void (như System.out.printf()).

phiên bản đầu tiên của bạn về cơ bản sử dụng một biểu thức với một chút chi phí:

i -> i = i * 2 có thể được giảm xuống còn i -> i * 2, như sự phân công i = chỉ là không cần thiết, bởi vì i biến mất ngay sau đó mà không được sử dụng bất kỳ hơn nữa.

Nó cũng giống như

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

hoặc

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

có thể được đơn giản hóa để

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

Tất cả các ví dụ này sẽ phù hợp với giao diện UnaryOperator<Integer> mà là một trường hợp đặc biệt cho Function<Integer, Integer> .

Ngược lại, bạn ví dụ 2 là như

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

mà không làm việc:

  • Hoặc XYvoid (mà sẽ làm cho một Consumer<Integer> mà không phù hợp với .map())
  • Hoặc XY thực sự là Integer (sau đó báo cáo trả về bị thiếu).

Trường hợp cần thiết cho giá trị đó (ngoại trừ việc biên dịch mã)?

Vâng, .forEach(System.out::println); cần mà giá trị ...

Vì vậy, tất cả mọi thứ mà có thể được chuyển đổi sang một Function<T, R> thể được trao cho một .map() của một dòng T, dẫn đến một R dòng:

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

chuyển số Integer mà bạn đưa vào một số Integer khác, cung cấp cho bạn Stream<Integer> khác. Bạn nhận thấy rằng chúng được đóng hộp?

cách khác sẽ

// 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; }) 

Tất cả các ví dụ này có điểm chung mà họ nhận được một giá trị và tạo ra một giá trị của một trong hai giống nhau hoặc một loại khác nhau. (Lưu ý cách các ví dụ sử dụng autoboxing và AutoUnboxing khi cần thiết.)

OTOH, tất cả mọi thứ mà có thể được chuyển đổi sang một Consumer<T> thể được trao cho một .map() của một dòng T, mà có thể là bất kỳ hình thức lambda trong đó sản xuất một biểu void :

.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 

với ý nghĩ đó, nó rất dễ dàng để thấy rằng một lambda với void kiểu biểu thức không thể được trao cho .map() và lambda với một phi void loại không thể được trao cho forEach().

+3

Vâng, ngay cả khi không có thiết bị đầu cuối '.forEach (Hệ thống.out :: println) ', thực tế là' map (…) 'mong đợi một * Hàm * đủ để yêu cầu biểu thức lambda trả về một giá trị… – Holger

+0

Tôi đã chỉnh sửa câu hỏi của mình để bạn có thể muốn chỉnh sửa câu trả lời của mình :) –

+0

@ BenWin Tôi đã cố gắng để làm sáng tỏ một số điều đó và hy vọng sẽ thành công trong đó ... – glglgl

Các vấn đề liên quan