Giới thiệu về các luồng Java trong Java 8
Luồng Java, được giới thiệu như một phần của Java 8, được sử dụng để làm việc với các bộ sưu tập dữ liệu. Bản thân chúng không phải là một cấu trúc dữ liệu nhưng có thể được sử dụng để nhập thông tin từ các cấu trúc dữ liệu khác bằng cách sắp xếp thứ tự và tạo đường dẫn để tạo ra kết quả cuối cùng. Lưu ý: Điều quan trọng là không nhầm lẫn giữa Luồng và Chủ đề, vì trong tiếng Nga cả hai thuật ngữ này thường được gọi trong cùng một bản dịch là “dòng chảy”. Luồng biểu thị một đối tượng để thực hiện các thao tác (thường xuyên nhất là truyền hoặc lưu trữ dữ liệu), trong khi Thread (dịch theo nghĩa đen - thread) biểu thị một đối tượng cho phép mã chương trình nhất định được thực thi song song với các nhánh mã khác. Vì Stream không phải là cấu trúc dữ liệu riêng biệt nên nó không bao giờ thay đổi nguồn dữ liệu. Các luồng Java có các tính năng sau:-
Java Stream có thể được sử dụng bằng cách sử dụng gói “java.util.stream”. Nó có thể được nhập vào tập lệnh bằng mã:
import java.util.stream.* ;
Sử dụng mã này, chúng ta cũng có thể dễ dàng triển khai một số hàm dựng sẵn trong Java Stream.
-
Luồng Java có thể chấp nhận đầu vào từ các bộ sưu tập dữ liệu như bộ sưu tập và mảng trong Java.
-
Luồng Java không yêu cầu thay đổi cấu trúc dữ liệu đầu vào.
-
Luồng Java không thay đổi nguồn. Thay vào đó, nó tạo ra đầu ra bằng các phương pháp đường ống thích hợp.
-
Luồng Java phụ thuộc vào các hoạt động trung gian và đầu cuối mà chúng ta sẽ thảo luận trong các phần sau.
-
Trong Luồng Java, các hoạt động trung gian được sắp xếp theo đường dẫn và diễn ra ở định dạng đánh giá từng phần. Chúng kết thúc bằng các chức năng đầu cuối. Điều này tạo thành định dạng cơ bản để sử dụng Java Stream.
Tạo luồng Java trong Java 8
Các luồng Java có thể được tạo theo nhiều cách:1. Tạo một luồng trống bằng phương thức Stream.empty()
Bạn có thể tạo một luồng trống để sử dụng sau này trong mã của mình. Nếu bạn sử dụng phương thức Stream.empty() , một luồng trống sẽ được tạo, không chứa giá trị nào. Luồng trống này có thể hữu ích nếu chúng ta muốn bỏ qua ngoại lệ con trỏ null khi chạy. Để làm điều này bạn có thể sử dụng lệnh sau:Stream<String> str = Stream.empty();
Câu lệnh trên sẽ tạo ra một luồng trống có tên str mà không có bất kỳ phần tử nào bên trong nó. Để xác minh điều này, chỉ cần kiểm tra số lượng hoặc kích thước của luồng bằng thuật ngữ str.count() . Ví dụ,
System.out.println(str.count());
Kết quả là chúng ta nhận được 0 ở đầu ra .
2. Tạo luồng bằng phương thức Stream.builder() với phiên bản Stream.Builder
Chúng tôi cũng có thể sử dụng Trình tạo luồng để tạo luồng bằng mẫu thiết kế của trình tạo. Nó được thiết kế để xây dựng các đối tượng theo từng bước. Hãy xem cách chúng ta có thể khởi tạo một luồng bằng cách sử dụng Stream Builder .Stream.Builder<Integer> numBuilder = Stream.builder();
numBuilder.add(1).add(2).add( 3);
Stream<Integer> numStream = numBuilder.build();
Sử dụng mã này, bạn có thể tạo luồng có tên numStream chứa các phần tử int . Mọi thứ được thực hiện khá nhanh chóng nhờ vào instance Stream.Builder có tên numBuilder được tạo trước tiên.
3. Tạo luồng với các giá trị được chỉ định bằng phương thức Stream.of()
Một cách khác để tạo luồng liên quan đến việc sử dụng phương thức Stream.of() . Đây là cách đơn giản để tạo luồng với các giá trị nhất định. Nó khai báo và khởi tạo thread. Một ví dụ về cách sử dụng phương thức Stream.of() để tạo luồng:Stream<Integer> numStream = Stream.of(1, 2, 3);
Mã này sẽ tạo một luồng chứa các phần tử int , giống như chúng ta đã làm trong phương thức trước đó bằng cách sử dụng Stream.Builder . Ở đây chúng ta đã trực tiếp tạo một luồng bằng cách sử dụng Stream.of() với các giá trị được xác định trước [1, 2 và 3] .
4. Tạo luồng từ một mảng hiện có bằng phương thức Arrays.stream()
Một phương pháp phổ biến khác để tạo một luồng liên quan đến việc sử dụng mảng trong Java. Luồng ở đây được tạo từ một mảng hiện có bằng phương thức Arrays.stream() . Tất cả các phần tử mảng được chuyển đổi thành phần tử luồng. Đây là một ví dụ điển hình:Integer[] arr = {1, 2, 3, 4, 5};
Stream<Integer> numStream = Arrays.stream(arr);
Mã này sẽ tạo ra một numStream chứa nội dung của một mảng có tên là arr, là một mảng số nguyên.
5. Hợp nhất hai luồng hiện có bằng phương thức Stream.concat()
Một phương thức khác có thể được sử dụng để tạo luồng là phương thức Stream.concat() . Nó được sử dụng để kết hợp hai luồng để tạo một luồng duy nhất. Cả hai luồng được kết hợp theo thứ tự. Điều này có nghĩa là luồng đầu tiên xuất hiện trước, tiếp theo là luồng thứ hai, v.v. Một ví dụ về nối như thế này:Stream<Integer> numStream1 = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> numStream2 = Stream.of(1, 2, 3);
Stream<Integer> combinedStream = Stream.concat( numStream1, numStream2);
Câu lệnh trên sẽ tạo luồng cuối cùng có tên là tổ hợpStream chứa từng phần tử của luồng đầu tiên numStream1 và luồng thứ hai numStream2 .
Các loại hoạt động với Java Stream
Như đã đề cập, bạn có thể thực hiện hai loại hoạt động với Java Stream trong Java 8: trung gian và thiết bị đầu cuối. Chúng ta hãy xem xét từng người trong số họ chi tiết hơn.Hoạt động trung gian
Các thao tác trung gian tạo ra luồng đầu ra và chỉ được thực thi khi gặp thao tác đầu cuối. Điều này có nghĩa là các hoạt động trung gian được thực hiện một cách lười biếng, theo đường dẫn và chỉ có thể được hoàn thành bằng thao tác đầu cuối. Bạn sẽ tìm hiểu về đánh giá lười biếng và quy trình hóa sau này. Ví dụ về các hoạt động trung gian là các phương thức sau: filter() , map() , Different() , eek() , sort() và một số phương thức khác.Hoạt động của nhà ga
Các hoạt động của thiết bị đầu cuối hoàn thành việc thực hiện các hoạt động trung gian và cũng trả về kết quả cuối cùng của luồng đầu ra. Bởi vì các hoạt động của thiết bị đầu cuối báo hiệu sự kết thúc của quá trình thực thi lười biếng và đường dẫn, luồng này không thể được sử dụng lại sau khi nó đã trải qua một hoạt động cuối cùng. Ví dụ về hoạt động của thiết bị đầu cuối là các phương thức sau: forEach() , coll() , count() , less() v.v.Ví dụ về các thao tác với Java Stream
Hoạt động trung gian
Dưới đây là một số ví dụ về một số thao tác trung gian có thể được áp dụng cho Luồng Java:lọc()
Phương thức này được sử dụng để lọc các phần tử từ luồng khớp với một vị từ cụ thể trong Java. Những mục được lọc này sau đó sẽ tạo thành một luồng mới. Chúng ta hãy xem một ví dụ để hiểu rõ hơn.Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> even = numStream.filter(n -> n % 2 == 0) .collect(Collectors.toList()); System.out.println(even);
Phần kết luận:
bản đồ()
Phương thức này được sử dụng để tạo luồng mới bằng cách thực thi các hàm được ánh xạ trên các phần tử của luồng đầu vào ban đầu. Có lẽ luồng mới có kiểu dữ liệu khác. Ví dụ trông như thế này:Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> d = numStream.map(n -> n*2) .collect(Collectors.toList()); System.out.println(d);
Phần kết luận:
riêng biệt()
Phương pháp này được sử dụng để chỉ truy xuất các phần tử riêng lẻ trong luồng bằng cách lọc ra các phần tử trùng lặp. Một ví dụ tương tự trông như thế này:Stream<Integer> numStream = Stream.of(43,65,1,98,63,63,1); List<Integer> numList = numStream.distinct() .collect(Collectors.toList()); System.out.println(numList);
Phần kết luận:
nhìn trộm()
Phương pháp này được sử dụng để theo dõi các thay đổi trung gian trước khi thực hiện thao tác đầu cuối. Điều này có nghĩa là seek() có thể được sử dụng để thực hiện một thao tác trên từng thành phần của luồng nhằm tạo luồng trên đó có thể thực hiện các thao tác trung gian tiếp theo.Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> nList = numStream.map(n -> n*10) .peek(n->System.out.println("Mapped: "+ n)) .collect(Collectors.toList()); System.out.println(nList);
Phần kết luận:
được sắp xếp()
Phương thức được sắp xếp() được sử dụng để sắp xếp các phần tử của luồng. Theo mặc định, nó sắp xếp các phần tử theo thứ tự tăng dần. Bạn cũng có thể chỉ định thứ tự sắp xếp cụ thể làm tham số. Một ví dụ thực hiện phương pháp này trông như thế này:Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.sorted().forEach(n -> System.out.println(n));
Phần kết luận:
Hoạt động của nhà ga
Dưới đây là một số ví dụ về một số thao tác đầu cuối có thể được áp dụng cho luồng Java:cho mỗi()
Phương thức forEach() được sử dụng để lặp qua tất cả các phần tử của luồng và thực thi từng hàm trên từng phần tử một. Điều này hoạt động như một giải pháp thay thế cho các câu lệnh lặp như for , while và các câu lệnh khác. Ví dụ:Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.forEach(n -> System.out.println(n));
Phần kết luận:
đếm()
Phương thức count() được sử dụng để lấy tổng số phần tử có trong luồng. Nó tương tự như phương thức size() , thường được sử dụng để xác định tổng số phần tử trong một bộ sưu tập. Một ví dụ về việc sử dụng phương thức count() với Luồng Java như sau:Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); System.out.println(numStream.count());
Phần kết luận:
sưu tầm()
Phương thức coll() được sử dụng để thực hiện việc giảm các phần tử luồng có thể thay đổi. Nó có thể được sử dụng để xóa nội dung khỏi luồng sau khi quá trình xử lý hoàn tất. Nó sử dụng lớp Collector để thực hiện việc giảm bớt .Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> odd = numStream.filter(n -> n % 2 == 1) .collect(Collectors.toList()); System.out.println(odd);
Phần kết luận:
tối thiểu() và tối đa()
Phương thức min() , như tên cho thấy, có thể được sử dụng trên một luồng để tìm phần tử tối thiểu trong đó. Tương tự, phương thức max() có thể được sử dụng để tìm phần tử lớn nhất trong luồng. Hãy thử hiểu cách chúng có thể được sử dụng bằng một ví dụ:Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); int smallest = numStream.min((m, n) -> Integer.compare(m, n)).get(); System.out.println("Smallest element: " + smallest);
numStream = Stream.of(43, 65, 1, 98, 63); int largest = numStream.max((m, n) -> Integer.compare(m, n)).get(); System.out.println("Largest element: " + largest);
Phần kết luận:
findAny() và findFirst()
findAny() trả về bất kỳ phần tử nào của luồng dưới dạng Tùy chọn . Nếu luồng trống, nó cũng sẽ trả về giá trị Tùy chọn , giá trị này sẽ trống. findFirst() trả về phần tử đầu tiên của luồng dưới dạng Tùy chọn . Giống như phương thức findAny() , phương thức findFirst() cũng trả về một tham số Tùy chọn trống nếu luồng tương ứng trống. Chúng ta hãy xem ví dụ sau dựa trên các phương pháp này:Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); Optional<Integer> opt = numStream.findFirst();System.out.println(opt); numStream = Stream.empty(); opt = numStream.findAny();System.out.println(opt);
Phần kết luận:
allMatch() , AnyMatch() và noneMatch()
Phương thức allMatch() được sử dụng để kiểm tra xem tất cả các phần tử trong luồng có khớp với một vị từ nhất định hay không và trả về giá trị boolean true nếu chúng khớp với nhau, nếu không thì trả về false . Nếu luồng trống, nó sẽ trả về true . Phương thức AnyMatch() được sử dụng để kiểm tra xem bất kỳ phần tử nào trong luồng có khớp với một vị từ nhất định hay không. Nó trả về true nếu có, ngược lại là false . Nếu luồng trống, nó sẽ trả về false . Phương thức noneMatch() trả về true nếu không có phần tử nào trong luồng khớp với vị từ và trả về false nếu không. Một ví dụ để minh họa điều này trông như thế này:Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); boolean flag = numStream.allMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.anyMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.noneMatch(n -> n==1);System.out.println(flag);
Phần kết luận:
Đánh giá lười biếng trong luồng Java
Đánh giá lười biếng dẫn đến tối ưu hóa khi làm việc với Luồng Java trong Java 8. Chúng chủ yếu liên quan đến việc trì hoãn các hoạt động trung gian cho đến khi gặp phải thao tác đầu cuối. Đánh giá lười biếng có trách nhiệm ngăn chặn sự lãng phí tài nguyên không cần thiết khi tính toán cho đến khi thực sự cần kết quả. Luồng đầu ra từ các hoạt động trung gian chỉ được tạo sau khi hoạt động đầu cuối hoàn thành. Đánh giá lười biếng hoạt động với tất cả các hoạt động trung gian trong luồng Java. Việc sử dụng đánh giá lười biếng rất hữu ích xảy ra khi làm việc với các luồng vô hạn. Bằng cách này, rất nhiều quá trình xử lý không cần thiết sẽ được ngăn chặn.Đường dẫn trong luồng Java
Một quy trình trong Luồng Java bao gồm một luồng đầu vào, không hoặc nhiều hoạt động trung gian được xếp hàng lần lượt và cuối cùng là hoạt động đầu cuối. Các hoạt động trung gian trong Luồng Java được thực hiện một cách lười biếng. Điều này làm cho các hoạt động trung gian theo đường ống là không thể tránh khỏi. Với các đường ống, về cơ bản là các hoạt động trung gian được kết hợp theo thứ tự, việc thực thi lười biếng trở nên khả thi. Đường ống giúp theo dõi các hoạt động trung gian cần được thực hiện sau khi gặp phải hoạt động cuối cùng.Phần kết luận
Bây giờ chúng ta hãy tóm tắt những gì chúng ta đã học được ngày hôm nay. Trong bài viết này:- Chúng ta đã xem nhanh Java Stream là gì.
- Sau đó, chúng tôi đã học được nhiều kỹ thuật khác nhau để tạo các luồng Java trong Java 8.
- Chúng ta đã tìm hiểu hai loại hoạt động chính (các hoạt động trung gian và các hoạt động đầu cuối) có thể được thực hiện trên các luồng Java.
- Sau đó chúng tôi xem xét chi tiết một số ví dụ về cả hoạt động trung gian và hoạt động cuối cùng.
- Cuối cùng, chúng tôi đã tìm hiểu thêm về đánh giá lười biếng và cuối cùng là tìm hiểu về đường dẫn trong các luồng Java.
GO TO FULL VERSION