2015-02-28 41 views
30

Khi sử dụng các luồng Java 8, việc lấy danh sách, tạo luồng từ đó, thực hiện công việc và chuyển đổi trở lại là khá phổ biến. Một cái gì đó như:Tại sao Stream không có phương thức toList()?

Stream.of(-2,1,2,-5) 
     .filter(n -> n > 0) 
     .map(n -> n * n) 
     .collect(Collectors.toList()); 

Tại sao không có phương pháp cắt ngắn/thuận tiện cho phần '.collect(Collectors.toList())'? Trên giao diện Stream, có phương pháp để chuyển đổi kết quả thành mảng gọi là toArray(), tại sao thiếu toList()?

IMHO, việc chuyển đổi kết quả thành danh sách phổ biến hơn so với mảng. Tôi có thể sống với điều đó, nhưng nó khá là khó chịu khi gọi sự xấu xa này.

Bất kỳ ý tưởng nào?

+15

tôi sẽ đặt câu hỏi ngược lại: tại sao 'toArray() 'thay vì' thu thập (toArray ()) '. Sự bùng nổ API là điều mà JDK có xu hướng chống lại càng nhiều càng tốt. Tôi hy vọng có được sự biện minh tốt cho 'toArray()'. –

+10

Tại sao dừng lại trên 'toList'? Cho phép thêm 'toStack'' toSet' 'toMap'. – Pshemo

+0

@MarkoTopolnik đúng, thx, tôi đã chỉnh sửa câu trả lời –

Trả lời

3

Đối với "lý do", tôi tin rằng có khá nhiều đối số trong nhận xét. Tuy nhiên, tôi đồng ý với bạn rằng nó khá khó chịu khi không có phương thức toList(). Điều tương tự cũng xảy ra với phương thức toIterable().

Vì vậy, tôi sẽ chỉ cho bạn một mẹo cho phép bạn sử dụng hai phương pháp này. May mắn thay, Java là rất linh hoạt và cho phép bạn làm tất cả các loại công cụ thú vị. Khoảng 10 năm trước, tôi đọc this article, mô tả một thủ thuật dí dỏm để "cắm" các phương thức vào bất kỳ giao diện cụ thể nào. Bí quyết này bao gồm việc sử dụng proxy để điều chỉnh giao diện không có các phương thức bạn muốn. Trong những năm qua, tôi đã tìm thấy rằng nó có tất cả các ưu điểm của mẫu adapter, trong khi nó thiếu tất cả các khuyết điểm của nó. Đó là những gì tôi gọi là một việc lớn.

Dưới đây là một số mẫu mã, chỉ cần để hiển thị các ý tưởng:

public class Streams { 

    public interface EnhancedStream<T> 
     extends Stream<T> { 

     List<T> toList(); 

     Iterable<T> toIterable(); 
    } 

    @SuppressWarnings("unchecked") 
    public static <T> EnhancedStream<T> enhance(Stream<T> stream) { 

     return (EnhancedStream<T>) Proxy.newProxyInstance(
      EnhancedStream.class.getClassLoader(), 
      new Class<?>[] {EnhancedStream.class}, 
      (proxy, method, args) -> { 

      if ("toList".equals(method.getName())) { 

       return stream.collect(Collectors.toList()); 

      } else if ("toIterable".equals(method.getName())) { 

       return (Iterable<T>) stream::iterator; 

      } else { 
       // invoke method on the actual stream 
       return method.invoke(stream, args); 
      } 
     }); 
    } 

    public static void main(String[] args) { 

     Stream<Integer> stream1 = Stream.of(-2, 1, 2, -5). 
      filter(n -> n > 0).map(n -> n * n); 
     List<Integer> list = Streams.enhance(stream1).toList(); 
     System.out.println(list); // [1, 4] 

     Stream<Integer> stream2 = Stream.of(-2, 1, 2, -5). 
      filter(n -> n > 0).map(n -> n * n); 
     Iterable<Integer> iterable = Streams.enhance(stream2).toIterable(); 
     iterable.forEach(System.out::println); // 1 
               // 4 
    } 
} 

Ý tưởng là sử dụng một giao diện EnhancedStream mở rộng giao diện Java Stream bằng cách xác định phương pháp bạn muốn thêm vào. Sau đó, proxy động triển khai giao diện mở rộng này bằng cách ủy quyền các phương thức gốc Stream cho luồng thực tế đang được điều chỉnh, trong khi nó chỉ cung cấp triển khai nội tuyến cho các phương thức mới (những phương thức không được xác định trong Stream).

Proxy này có sẵn bằng phương tiện tĩnh minh bạch thực hiện tất cả nội dung proxy.

Xin lưu ý rằng tôi không nói rằng đây là giải pháp cuối cùng. Thay vào đó, nó chỉ là một ví dụ có thể được cải thiện rất cao, tức là đối với mọi phương thức Stream trả về một số Stream khác, bạn cũng có thể trả lại proxy cho phương thức đó. Điều này sẽ cho phép EnhancedStream s bị xiềng xích (bạn cần phải xác định lại các phương pháp này trong giao diện EnhancedStream, để chúng trả lại loại trả về tương đối EnhancedStream). Bên cạnh đó, xử lý ngoại lệ thích hợp bị thiếu, cũng như mã mạnh hơn để quyết định có ủy quyền thực hiện các phương thức cho luồng gốc hay không.

+0

Đây là một câu trả lời tuyệt vời! cám ơn vì đã chia sẻ. – asgs

10

Gần đây tôi đã viết một thư viện nhỏ gọi StreamEx kéo dài suối Java và cung cấp phương pháp này chính xác trong số rất nhiều các tính năng khác:

StreamEx.of(-2,1,2,-5) 
    .filter(n -> n > 0) 
    .map(n -> n * n) 
    .toList(); 

Cũng toSet(), toCollection (Nhà cung cấp), tham gia(), groupingBy() và các phương pháp tắt khác có sẵn ở đó.

1

Dưới đây là một lớp helper đơn giản mà làm cho điều này dễ dàng hơn: sử dụng

public class Li { 
    public static <T> List<T> st(final Stream<T> stream) { 
     return stream.collect(Collectors.toList()); 
    } 
} 

Ví dụ:

List<String> lowered = Li.st(Stream.of("HELLO", "WORLD").map(String::toLowerCase)); 
Các vấn đề liên quan