2015-06-10 20 views
7

Hãy xem xét ví dụ sau đó in các yếu tố tối đa trong một List:Lựa chọn giữa suối và Bộ sưu tập API

List<Integer> list = Arrays.asList(1,4,3,9,7,4,8);   
list.stream().max(Comparator.naturalOrder()).ifPresent(System.out::println); 

Mục tiêu tương tự cũng có thể đạt được bằng cách sử dụng Collections.max phương pháp:

System.out.println(Collections.max(list)); 

Trên đây mã không chỉ ngắn hơn mà còn sạch hơn để đọc (theo ý kiến ​​của tôi). Có những ví dụ tương tự mà bạn nghĩ đến như sử dụng binarySearchfilter được sử dụng cùng với findAny.

Tôi hiểu rằng Stream có thể là một đường ống vô hạn như trái ngược với Collection bị giới hạn bởi bộ nhớ có sẵn cho JVM. Đây sẽ là tiêu chí của tôi để quyết định có nên sử dụng một số Stream hoặc API Collections hay không. Có bất kỳ lý do nào khác để chọn Stream qua API Collections (chẳng hạn như hiệu suất) hay không. Nói chung, đây có phải là lý do duy nhất để chọn Stream qua API cũ hơn có thể thực hiện công việc một cách gọn gàng hơn và ngắn hơn không?

+0

Bạn chỉ hỏi về phương pháp cụ thể này? –

+0

Nếu lợi ích duy nhất của bạn là nhận giá trị 'max' từ danh sách, phương thức' Bộ sưu tập' là cách để đi. – GriffeyDog

+0

Ngoài ra, hành vi 'Tùy chọn' so với' NoSuchElementException'. –

Trả lời

5

API luồng giống như dao quân đội Thụy Sĩ: nó cho phép bạn thực hiện các hoạt động khá phức tạp bằng cách kết hợp các công cụ hiệu quả. Mặt khác, nếu bạn chỉ cần một tuốc nơ vít, có lẽ tuốc nơ vít độc lập sẽ thuận tiện hơn. API luồng bao gồm nhiều thứ (như distinct, sorted, hoạt động nguyên thủy, v.v.), nếu không bạn sẽ phải viết nhiều dòng và giới thiệu các biến số/cấu trúc dữ liệu trung gian và các vòng nhàm chán thu hút sự chú ý của lập trình viên từ thuật toán thực tế. Đôi khi, sử dụng API luồng có thể cải thiện hiệu suất ngay cả đối với mã tuần tự. Ví dụ: hãy xem xét một số API cũ:

class Group { 
    private Map<String, User> users; 

    public List<User> getUsers() { 
     return new ArrayList<>(users.values()); 
    } 
} 

Ở đây chúng tôi muốn trả lại tất cả người dùng của nhóm. Nhà thiết kế API đã quyết định trả lại List. Nhưng nó có thể được sử dụng bên ngoài theo nhiều cách khác nhau:

List<User> users = group.getUsers(); 
Collections.sort(users); 
someOtherMethod(users.toArray(new User[users.size])); 

Tại đây, nó được sắp xếp và chuyển đổi thành mảng để chuyển sang một số phương pháp khác đã xảy ra để chấp nhận một mảng. Ở một nơi khác getUsers() có thể được sử dụng như thế này:

List<User> users = group.getUsers(); 
for(User user : users) { 
    if(user.getAge() < 18) { 
     throw new IllegalStateException("Underage user in selected group!"); 
    } 
} 

Ở đây chúng tôi chỉ muốn tìm người dùng phù hợp với một số tiêu chí. Trong cả hai trường hợp sao chép vào trung gian ArrayList thực sự là không cần thiết. Khi chúng ta chuyển sang Java 8, chúng ta có thể thay thế getUsers() phương pháp với users():

public Stream<User> users() { 
    return users.values().stream(); 
} 

Và thay đổi mã gọi. Thứ nhất:

someOtherMethod(group.users().sorted().toArray(User[]::new)); 

Thứ hai:

if(group.users().anyMatch(user -> user.getAge() < 18)) { 
    throw new IllegalStateException("Underage user in selected group!"); 
} 

Bằng cách này nó không chỉ ngắn hơn, nhưng có thể làm việc nhanh hơn là tốt, bởi vì chúng ta bỏ qua việc sao chép trung gian.

Điểm khái niệm khác trong API luồng là mọi mã luồng được viết theo nguyên tắc có thể được song song chỉ bằng cách thêm bước parallel(). Tất nhiên điều này sẽ không luôn luôn tăng hiệu suất, nhưng nó sẽ giúp thường xuyên hơn tôi mong đợi.Thông thường, nếu hoạt động được thực hiện tuần tự cho 0.1ms or longer, nó có thể được hưởng lợi từ việc song song. Dù sao, chúng tôi đã không nhìn thấy cách đơn giản như vậy để làm lập trình song song trong Java trước đây.

2

Tất nhiên, nó luôn phụ thuộc vào hoàn cảnh. Đưa bạn ví dụ ban đầu:

List<Integer> list = Arrays.asList(1,4,3,9,7,4,8);   
list.stream().max(Comparator.naturalOrder()).ifPresent(System.out::println); 

Nếu bạn muốn làm điều cùng một cách hiệu quả, bạn sẽ sử dụng

IntStream.of(1,4,3,9,7,4,8).max().ifPresent(System.out::println); 

mà không liên quan đến bất kỳ auto-boxing. Nhưng nếu giả định của bạn là có một List<Integer> trước đó, đó có thể không phải là một tùy chọn, vì vậy nếu bạn chỉ quan tâm đến giá trị max, Collections.max có thể là lựa chọn đơn giản hơn.

Nhưng điều này sẽ dẫn đến câu hỏi tại sao bạn có List<Integer> trước đó. Có lẽ, đó là kết quả của mã cũ (hoặc mã mới được viết bằng cách sử dụng tư duy cũ), không có lựa chọn nào khác ngoài việc sử dụng quyền anh và Collection vì không có thay thế nào trong quá khứ?

Vì vậy, có lẽ bạn nên suy nghĩ về nguồn sản xuất bộ sưu tập, trước khi bận tâm với cách sử dụng nó (hoặc tốt, suy nghĩ về cả hai cùng một lúc).

Nếu tất cả những gì bạn có là Collection và tất cả những gì bạn cần là một hoạt động thiết bị đầu cuối đơn lẻ, thực hiện đơn giản Collection, bạn có thể sử dụng trực tiếp mà không cần bận tâm đến API Stream. Các nhà thiết kế API đã thừa nhận ý tưởng này khi họ thêm các phương thức như forEach(…) vào API Collection thay vì nhấn mạnh mọi người sử dụng stream().forEach(…). Và Collection.forEach(…) không phải là viết tắt đơn giản cho Collection.stream().forEach(…), trên thực tế, nó đã được xác định trên giao diện Iterable trừu tượng hơn mà thậm chí không có phương thức stream().

Btw., Bạn nên hiểu sự khác biệt giữa Collections.binarySearchStream.filter/findAny. Trước đây yêu cầu bộ sưu tập là được sắp xếp và nếu điều kiện tiên quyết đó được đáp ứng, có thể là lựa chọn tốt hơn. Nhưng nếu bộ sưu tập không được sắp xếp, tìm kiếm tuyến tính đơn giản hiệu quả hơn việc phân loại chỉ cho một lần sử dụng tìm kiếm nhị phân, không phải để nói thực tế, tìm kiếm nhị phân đó chỉ hoạt động với List s trong khi lọc/tìmBất cứ công việc nào với bất kỳ luồng nào hỗ trợ mọi loại sưu tập nguồn.

+0

Bạn có đề xuất rằng bất kỳ mã mới nào mà tôi viết từ nay không nên sử dụng một 'Danh sách' hoặc 'Bộ sưu tập' nhưng luôn sử dụng' Luồng'? – CKing

+0

Không phải lúc nào cũng dễ dàng. Đó là những gì tôi đã cố gắng giải thích. Nếu hoạt động của bạn kết hợp các kiểu dữ liệu nguyên thủy và thao tác có thể được biểu diễn dưới dạng hoạt động của luồng mà không có bất kỳ quyền nào, điều đó là thích hợp hơn. Nếu hoạt động của bạn bao gồm nhiều bước hoặc kết hợp các chức năng, API luồng có thể là cách để đi. Nếu nguồn dữ liệu của bạn là một tập hợp hoặc mảng hiện có và hoạt động dự định có thể được biểu diễn dưới dạng một lời gọi đơn của phương thức hiện có (dựa trên bộ sưu tập), hãy sử dụng phương thức đó. Nếu bạn muốn sửa đổi bộ sưu tập tại chỗ, hãy ở lại API thu thập. – Holger

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