2014-09-17 31 views
6

Tôi đang cố gắng tính toán phép nhân của một giá trị bằng cách sử dụng hai giá trị trước đó bằng cách sử dụng luồng của java 8. Tôi muốn gọi một hàm sẽ trả về một mảng/danh sách/bộ sưu tập. Tôi đang tạo Danh sách và thêm 1,2 vào danh sách đó.Truyền một bộ sưu tập bằng cách sử dụng hàm reduce (3 parameters) - stream java 8

Giả sử tên danh sách là kết quả.

public static void main (String[] args) { 
List<Integer> result = new ArrayList<Integer>(); 
result.add(1); 
result.add(2); 
int n = 5; //n can be anything, choosing 5 for this example 
res(n, result); 
//print result which should be [1, 2, 2, 4, 8] 
} 
public static List<Integer> res(int n, List<Integer> result) { 
      result.stream() 
       .limit(n) 
       .reduce(identity, (base,index) -> base); 

     //return result; 
} 

Bây giờ vấn đề đang cố gắng chuyển kết quả vào luồng để tiếp tục cập nhật danh sách với các giá trị mới bằng cách sử dụng luồng. Theo hướng dẫn java, có thể, mặc dù không hiệu quả.

"Nếu hoạt động giảm của bạn liên quan đến việc thêm yếu tố vào bộ sưu tập, thì mỗi lần hàm tích lũy của bạn xử lý một phần tử, nó sẽ tạo bộ sưu tập mới bao gồm phần tử không hiệu quả."

Tôi có cần phải sử dụng tham số thứ ba tùy chọn, bộ phối hợp nhị phân, để kết hợp danh sách + kết quả không ??

<U> U reduce(U identity, 
     BiFunction<U,? super T,U> accumulator, 
     BinaryOperator<U> combiner) 

Tóm lại; Tôi muốn chuyển một danh sách với hai giá trị và có hàm tìm phép nhân của hai giá trị đầu tiên (1,2), thêm nó vào danh sách và tìm phép nhân của hai giá trị cuối cùng (2,2) và thêm nó vào danh sách và cho đến khi luồng đạt đến giới hạn.

+0

Được coi là thực tế rằng 'reduce' có thể không phải là công cụ thích hợp để thực hiện chức năng này? –

Trả lời

11

Dường như bạn đang cố triển khai mối quan hệ lặp lại. Phương pháp reduce áp dụng một số chức năng cho một loạt các giá trị đã tồn tại từ trước trong luồng. Bạn không thể sử dụng reduce và lấy kết quả trung gian từ chức năng giảm tốc và "nạp lại" vào luồng, đó là những gì bạn cần làm để thực hiện mối quan hệ lặp lại.

Cách để thực hiện quan hệ lặp lại bằng cách sử dụng luồng là sử dụng một trong các phương pháp nhà máy của luồng Stream.generate hoặc Stream.iterate. Nhà máy iterate dường như gợi ý cách tiếp cận rõ ràng nhất. Nhà nước mà cần phải được lưu giữ cho mỗi ứng dụng của hàm tái yêu cầu hai ints trong ví dụ của bạn, vì vậy không may chúng ta phải tạo ra một đối tượng để tổ chức này cho chúng tôi:

static class IntPair { 
    final int a, b; 
    IntPair(int a_, int b_) { 
     a = a_; b = b_; 
    } 
} 

Sử dụng đối tượng trạng thái này, bạn có thể tạo một dòng mà thực hiện sự tái phát mà bạn muốn:

Stream.iterate(new IntPair(1, 2), p -> new IntPair(p.b, p.a * p.b)) 

một khi bạn có một dòng như vậy, đó là một vấn đề đơn giản để thu thập các giá trị vào một danh sách:

List<Integer> output = 
    Stream.iterate(new IntPair(1, 2), p -> new IntPair(p.b, p.a * p.b)) 
      .limit(5) 
      .map(pair -> pair.a) 
      .collect(Collectors.toList()); 
System.out.println(output); 

[1, 2, 2, 4, 8] 

Ngoài ra, bạn có thể sử dụng kỹ thuật tương tự để tạo chuỗi Fibonacci. Tất cả bạn cần làm là cung cấp một giá trị khởi đầu và lặp chức năng khác nhau:

Stream.iterate(new IntPair(0, 1), p -> new IntPair(p.b, p.a + p.b)) 

Bạn cũng có thể thực hiện một mối quan hệ tái phát tương tự như sử dụng Stream.generate. Điều này cũng sẽ yêu cầu một lớp trợ giúp. Lớp trợ giúp triển khai Supplier giá trị kết quả nhưng nó cũng cần duy trì trạng thái. Do đó, nó cần phải có thể thay đổi, đó là loại tổng trong cuốn sách của tôi. Hàm lặp cũng cần được đưa vào đối tượng trình tạo. Điều này làm cho nó ít linh hoạt hơn đối tượng IntPair, có thể được sử dụng để tạo các lần lặp lại tùy ý.

+0

giải pháp tốt đẹp. Tôi đã suy nghĩ thay vì tạo một lớp khác, có lẽ chúng ta có thể sử dụng luồng concat và tái sử dụng các phần tử của luồng? – Dinesh

4

Chỉ cần cho đầy đủ, đây là giải pháp không cần thêm lớp.

List<Integer> output = Stream.iterate(
    (ToIntFunction<IntBinaryOperator>)f -> f.applyAsInt(1, 2), 
    prev -> f -> prev.applyAsInt((a, b) -> f.applyAsInt(b, a*b)) 
) 
.limit(9).map(pair -> pair.applyAsInt((a, b)->a)) 
.collect(Collectors.toList()); 

Đây là phương pháp chức năng không cần bộ nhớ giá trị trung gian. Tuy nhiên, vì Java không phải là ngôn ngữ lập trình chức năng và không có tối ưu hóa cho định nghĩa hàm đệ quy như vậy, điều này không được khuyến nghị cho các luồng lớn hơn.

Vì ví dụ này, luồng lớn hơn sẽ tràn số lượng và cách tính giá rẻ, phương pháp này hoạt động. Nhưng đối với các trường hợp sử dụng khác, bạn chắc chắn sẽ thích đối tượng lưu trữ khi giải quyết vấn đề với Java đơn giản (như trong Stuart Marks’ answer)

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