So sánh thao tác operajon và phương thức method năm 2024

Trong bài này chúng ta tiếp tục một tính năng mới trong Java 8 khác là Stream API. Nếu bạn chưa quen với các Lambda Expression, Functional Interface và Method Reference, thì bạn nên xem các bài viết đó trước khi bắt đầu với bài giới thiệu này.

Nội dung

Stream (luồng) là một đối tượng mới của Java được giới thiệu từ phiên bản Java 8, giúp cho việc thao tác trên collection và array trở nên dễ dàng và tối ưu hơn.

Một Stream đại diện cho một chuỗi các phần tử hỗ trợ các hoạt động tổng hợp tuần tự (sequential) và song song (parallel).

Tất cả các class và interface của Stream API nằm trong gói java.util.stream. Bằng cách sử dụng các stream, chúng ta có thể thực hiện các phép toán tổng hợp khác nhau trên dữ liệu được trả về từ các collection, array, các hoạt động Input/Output. Trước khi chúng ta thấy cách Stream API có thể được sử dụng trong Java như thế nào, hãy xem ví dụ sau để hiểu cách sử dụng Stream:

Ví dụ sử dụng Stream

package com.gpcoder.stream.intro; import java.util.Arrays; import java.util.List; public class StreamExample { List numbers = Arrays.asList(7, 2, 5, 4, 2, 1); public void withoutStream() {

long count = 0;
for (Integer number : numbers) {
  if (number % 2 == 0) {
    count++;
  }
}
System.out.printf("There are %d elements that are even", count);
} public void withStream() {
long count = numbers.stream().filter(num -> num % 2 == 0).count();
System.out.printf("There are %d elements that are even", count);
} }

Thực thi chương trình trên ta thấy cả hai phương thức đều cho kết quả giống nhau. Tuy nhiên có một sự khác biệt lớn giữa 2 cách này là cú pháp sử dụng stream sẽ ngắn gọn hơn, hiệu suất sử dụng stream tốt hơn.

Với phương thức withoutStream(), chúng ta đang lặp lại toàn bộ danh sách để tìm các số chẵn. Không có xử lý song song trong phương thức này.

Với phương thức withStream(), phương thức stream() trả về một luồng của tất cả các number, phương thức filter() trả về một stream các các số chẵn, phương thức count() trả về số phần tử là số chẵn của stream. Tất cả các hoạt động này đang xảy ra song song, có nghĩa là chúng ta có thể thực hiện song song code với sự trợ giúp của stream. Việc thực hiện song song các hoạt động sử dụng stream nhanh hơn thực thi tuần tự mà không cần sử dụng stream.

Một số phương thức của Stream

Trong Java 8, Collection interface được hỗ trợ 2 phương thức để tạo ra Stream bao gồm:

  • stream() : trả về một stream sẽ được xử lý theo tuần tự.
  • parallelStream() : trả về một Stream song song, các xử lý sau đó sẽ thực hiện song song.

Các đặc điểm của Java Stream

  • Stream không lưu trữ các phần tử của collection hay array. Nó chỉ thực hiện các phép toán tổng hợp (chẳng hạn như filter() và count() mà chúng ta đã thấy trong ví dụ trên để có được stream dữ liệu mong muốn.
  • Stream không phải là một cấu trúc dữ liệu (data structure).
  • Stream là immutable object. Các hoạt động tổng hợp mà chúng ta thực hiện trên Collection, Array hoặc bất kỳ nguồn dữ liệu nào khác không làm thay đổi dữ liệu của nguồn, chúng chỉ trả lại stream mới. Chúng ta đã thấy ở ví dụ trên là thực hiện filter() các các số chẵn bằng cách sử dụng các hoạt động của stream nhưng nó không thay đổi các phần tử của List numbers.
  • Tất cả các hoạt động stream là lazy (lười biếng), có nghĩa là chúng không được thực hiện cho đến khi cần thiết. Để làm được điều này, hầu hết các thao tác với Stream đều return lại một Stream mới, giúp tạo một mắc xích bao gồm một loạt các thao tác nhằm thực thi các thao tác đó một cách tối ưu nhất. Mắc xích này còn được gọi là pipeline.
  • Các phần tử của luồng chỉ được truy cập một lần trong suốt vòng đời của Stream. Giống như một Iterator, một Stream mới phải được tạo ra để duyệt lại các phần tử của dữ liệu nguồn.
  • Stream không dùng lại được, nghĩa là một khi đã sử dụng nó xong, chúng ta không thể gọi nó lại để sử dụng lần nữa.
  • Chúng ta không thể dùng index để truy xuất các phần tử trong Stream.
  • Stream hỗ trợ thao tác song song các phần tử trong Collection hay Array.

So sánh Streams với Collections

Chúng ta sử dụng Stream hoặc Collection khi chúng ta phải làm việc theo danh sách các phần tử.

Collection là cấu trúc dữ liệu chứa các phần tử trong bộ nhớ. Những phần tử này sẽ được tính toán trước khi chúng thực sự được thêm vào Collection.

Ngược lại, Stream không phải là một cấu trúc dữ liệu. Stream là một luồng thực hiện tính toán các phần tử theo yêu cầu. Vì vậy, nó có thể được xem rằng các Collection có các yếu tố tính tức thời (eager), trong khi các Stream có yếu tố tính lười biếng (lazy).

Mặc dù chúng ta có thể tạo Stream từ Collection và sử dụng một số phương thức trên Collection. Tuy nhiên, Collection gốc vẫn không thay đổi. Do đó, Stream không thể thay đổi dữ liệu.

Và một đặc điểm quan trọng của Stream là chúng có thể chuyển đổi dữ liệu, vì các hoạt động trên Stream có thể tạo ra một cấu trúc dữ liệu khác, như map() và collect() như trong các ví dụ trên.

Cách làm việc với Stream trong Java

Như chúng ta đã thấy trong ví dụ trên, hoạt động của luồng có thể được giải thích theo ba giai đoạn:

  • Tạo Stream (stream source).
  • Thực hiện các thao tác trung gian (intermediate operations) trên stream ban đầu để chuyển đổi nó thành một stream khác và tiếp tục thực hiện các hoạt động trung gian khác. Trong ví dụ trên, hoạt động filter() là hoạt động trung gian. Có thể có nhiều hoạt động trung gian.
  • Thực hiện thao tác đầu cuối (terminal operation) trên stream cuối cùng để nhận kết quả và sau đó bạn không thể sử dụng lại chúng. Trong ví dụ trên, phép tính count() là hoạt động đầu cuối.

Một Stream pipeline bao gồm: 1 stream source, 0 hoặc nhiều intermediate operation, và 1 terminal operation.

Hình dưới minh họa các thao tác với Stream:

So sánh thao tác operajon và phương thức method năm 2024

Một vài ví dụ về Stream

Tạo Stream

Interface Stream trong package java.util.stream là interface đại diện cho một Stream. Interface này chỉ làm việc với kiểu dữ liệu là Object.

Với các kiểu primitive thì các bạn có thể sử dụng các đối tượng Stream dành cho những kiểu primitive đó, ví dụ như IntStream, LongStream hay DoubleStream.

Tạo Stream cho những kiểu primitive

package com.gpcoder.stream.intro; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; public class PrimitiveStreamExample { public static void main(String[] args) {

IntStream.range(1, 4).forEach(System.out::println); // 1 2 3
IntStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
DoubleStream.of(1, 2, 3).forEach(System.out::println); // 1.0 2.0 3.0
LongStream.range(1, 4).forEach(System.out::println); // 1 2 3
LongStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
} }

Tạo Stream từ các cấu trúc dữ liệu khác

package com.gpcoder.stream.convert; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; import java.util.stream.Stream; public class ConvertToStreamExample { // Generate Streams from Arrays using .stream or Stream.of public static void streamFromArray() {

String[] languages = { "Java", "C#", "C++", "PHP", "Javascript" };
// Get Stream using the Arrays.stream
Stream testStream1 = Arrays.stream(languages);
testStream1.forEach(x -> System.out.println(x));
// Get Stream using the Stream.of
Stream testStream2 = Stream.of(languages);
testStream2.forEach(x -> System.out.println(x));
} // Generate Streams from Collections public static void streamFromCollection() {
List items = new ArrayList<>();
items.add("Java");
items.add("C#");
items.add("C++");
items.add("PHP");
items.add("Javascript");
items.stream().forEach(item -> System.out.println(item));
} // Generate Streams using Stream.generate() public static void streamUsingGenerate() {
Stream stream = Stream.generate(() -> "gpcoder").limit(3);
String[] testStrArr = stream.toArray(String[]::new);
System.out.println(Arrays.toString(testStrArr)); // [gpcoder, gpcoder, gpcoder]
} // Generate Streams using Stream.iterate() public static void streamUsingIterate() {
Stream iterateNumbers = Stream.iterate(1L, n -> n + 1).limit(5);
iterateNumbers.forEach(System.out::print); // 12345
} // Generate Streams from APIs like Regex public static void streamUsingRegex() {
String str = "Welcome,to,gpcoder";
Pattern.compile(",").splitAsStream(str).forEach(System.out::print);// Welcometogpcoder
} }

Tạo Stream sang các cấu trúc dữ liệu khác

package com.gpcoder.stream.convert; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public class ConvertFromStreamExample { // Get Collections using stream.collect(Collectors.toList()) public static void getCollectionUsingStreamCollection() {

Stream stream = Stream.of("Java", "C#", "C++", "PHP", "Javascript");
List languages = stream.collect(Collectors.toList());
System.out.println(languages);
} // Get arrays using stream.toArray(EntryType[]::new) public static void getArrayUsingStreamToArray() {
Stream stream = Stream.of("Java", "C#", "C++", "PHP", "Javascript");
String[] languages = stream.toArray(String[]::new);
System.out.println(Arrays.toString(languages));
} }

Ví dụ Intermediate operations

Có thể sử dụng 0 hoặc nhiều intermediate operations để chuyển đổi Stream ban đầu thành những Stream mới. Mặc dù chúng ta có thể định nghĩa nhiều intermediate operation nhưng chúng không thực thi các thao tác đó ngay lập tức, chỉ khi terminal operation được gọi thì toàn bộ các thao tác đó mới được thực thi,

Sử dụng filter()

Stream filter() giúp loại bỏ các phần tử dựa trên các tiêu chí nhất định.

Ví dụ sử dụng để lọc các số chia hết cho 3.

package com.gpcoder.stream.intermediate; import java.util.stream.Stream; public class FilterStreamExample { // filter() operation helps eliminate elements based on certain criteria public static void main(String[] args) {

Stream.iterate(1, count -> count + 1) //
    .filter(number -> number % 3 == 0) //
    .limit(6) //
    .forEach(System.out::println);
} }

Sử dụng skip(), limit()

Ý nghĩa của Stream skip(), limit() hoàn toàn tương tự với OFSET và LIMIT trong SQL.

Stream limit() được sử dụng để loại bỏ các phần tử n đầu tiên của Stream . Nếu Stream này chứa ít hơn n phần tử thì luồng trống sẽ được trả lại.

Stream limit() được sử dụng để cắt giảm kích thước của Stream. Kết quả trả về các phần tử của Stream đã được cắt giảm để không vượt quá maxSize (tham số đầu vào của phương thức).

package com.gpcoder.stream.intermediate; import java.util.Arrays; import java.util.List; public class LimitStreamExample { // limit() Returns a stream consisting of the elements of this stream, truncated // to be no longer than maxSize in length. public static void main(String[] args) {

List data = Arrays.asList("Java", "C#", "C++", "PHP", "Javascript");
data.stream() //
    .skip(1) //
    .limit(3) //
    .forEach(System.out::print); // C
# C++PHP } }

Sử dụng map()

Stream map() giúp ánh xạ các phần tử tới các kết quả tương ứng.

package com.gpcoder.stream.intermediate; import java.util.Arrays; import java.util.List; public class MapStreamExample { // map() operation helps map elements to the corresponding results public static void main(String[] args) {

List data = Arrays.asList("Java", "C#", "C++", "PHP", "Javascript");
data.stream() //
    .map(String::toUpperCase) // convert each element to upper case
    .forEach(System.out::println);
} }

Sử dụng sorted()

Stream sorted() giúp sắp xếp các phần tử theo một thứ tự xác định.

package com.gpcoder.stream.intermediate; import java.util.Arrays; import java.util.List; public class SortedStreamExample { // sorted() operation helps sort elements based on certain criteria public static void main(String[] args) {

List data = Arrays.asList("Java", "C#", "C++", "PHP", "Javascript");
// sorted according to natural order
data.stream() //
    .sorted() //
    .forEach(System.out::println);
// sorted according to the provided Comparator
data.stream() //
    .sorted((s1, s2) -> s1.length() - s2.length()) //
    .forEach(System.out::println);
} }

Ví dụ Terminal Operations

Sử dụng terminal operation lấy về kết quả từ những intermediate operations đã định nghĩa.

Chúng ta có thể dễ dàng xác định đâu là intermediate operation, đâu là terminal operation bởi vì terminal operation sẽ trả về void hoặc non-Stream object.

Sau khi terminal operation được gọi, Stream sẽ không sử dụng được nữa.

Sử dụng forEach()

Phương thức forEach() giúp duyệt qua các phần tử của Stream.

package com.gpcoder.stream.terminal; import java.util.stream.Stream; public class ForEachStreamExample { // foreach operations helps iterate the elements of the Stream public static void main(String[] args) {

Stream.iterate(1, count -> count + 1) //
    .filter(number -> number % 3 == 0) //
    .limit(6) //
    .forEach(System.out::println);
} }

Sử dụng collect()

Phương thức collect() giúp thu thập kết quả Stream sang một Collection.

package com.gpcoder.stream.terminal; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public class CollectStreamExample { // collect() operation helps to collect the stream result in a collection like // list public static void main(String[] args) {

Stream stream = Stream.of("Java", "C#", "C++", "PHP", "Javascript");
List languages = stream.collect(Collectors.toList());
System.out.println(languages);
} }

Sử dụng anyMatch(), allMatch(), noneMatch()

Phương thức anyMatch() trả về một boolean tùy thuộc vào điều kiện được áp dụng trên Stream dữ liệu. Phương thức này trả về true ngay khi phần tử đầu tiên thõa mãn điều kiện, những phần tử còn lại sẽ không được kiểm tra.

package com.gpcoder.stream.intro; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; public class PrimitiveStreamExample { public static void main(String[] args) {

IntStream.range(1, 4).forEach(System.out::println); // 1 2 3
IntStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
DoubleStream.of(1, 2, 3).forEach(System.out::println); // 1.0 2.0 3.0
LongStream.range(1, 4).forEach(System.out::println); // 1 2 3
LongStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
} }

0

Tương tự chúng ta có:

  • allMatch() : Phương thức này trả về true nếu tất cả phần tử đều thõa mãn điều kiện. Nếu một phần tử không thõa điều kiện, những phần tử còn lại sẽ không được kiểm tra.
  • noneMatch() : Ngược lại với allMatch(), pương thức này trả về true nếu tất cả phần tử đều không thõa mãn điều kiện. Nếu một phần tử thõa điều kiện, những phần tử còn lại sẽ không được kiểm tra.

Sử dụng count()

Phương thức count() trả về tổng số phần tử cho dữ liệu luồng.

package com.gpcoder.stream.intro; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; public class PrimitiveStreamExample { public static void main(String[] args) {

IntStream.range(1, 4).forEach(System.out::println); // 1 2 3
IntStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
DoubleStream.of(1, 2, 3).forEach(System.out::println); // 1.0 2.0 3.0
LongStream.range(1, 4).forEach(System.out::println); // 1 2 3
LongStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
} }

1

Sử dụng min(), max()

Stream.max(), Stream.max() chấp nhận đối số là một sao cho các item trong stream có thể được so sánh với nhau để tìm tối thiểu (min) hoặc tối đa (max).

Ví dụ sử dụng min(), max() với các class wrapper của kiểu dữ liệu nguyên thủy (primitive type):

package com.gpcoder.stream.intro; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; public class PrimitiveStreamExample { public static void main(String[] args) {

IntStream.range(1, 4).forEach(System.out::println); // 1 2 3
IntStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
DoubleStream.of(1, 2, 3).forEach(System.out::println); // 1.0 2.0 3.0
LongStream.range(1, 4).forEach(System.out::println); // 1 2 3
LongStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
} }

2

Ví dụ sử dụng min(), max() với các class Object:

package com.gpcoder.stream.intro; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; public class PrimitiveStreamExample { public static void main(String[] args) {

IntStream.range(1, 4).forEach(System.out::println); // 1 2 3
IntStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
DoubleStream.of(1, 2, 3).forEach(System.out::println); // 1.0 2.0 3.0
LongStream.range(1, 4).forEach(System.out::println); // 1 2 3
LongStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
} }

3

Sử dụng summaryStatistics()

Phương thức summaryStatistics() được sử dụng để lấy giá trị count, min, max, sum và average với tập dữ liệu số.

package com.gpcoder.stream.intro; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; public class PrimitiveStreamExample { public static void main(String[] args) {

IntStream.range(1, 4).forEach(System.out::println); // 1 2 3
IntStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
DoubleStream.of(1, 2, 3).forEach(System.out::println); // 1.0 2.0 3.0
LongStream.range(1, 4).forEach(System.out::println); // 1 2 3
LongStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
} }

4

Sử dụng reduce()

Phương thức reduce() kết hợp các phần tử luồng thành một bằng cách sử dụng một BinaryOperator.

package com.gpcoder.stream.intro; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; public class PrimitiveStreamExample { public static void main(String[] args) {

IntStream.range(1, 4).forEach(System.out::println); // 1 2 3
IntStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
DoubleStream.of(1, 2, 3).forEach(System.out::println); // 1.0 2.0 3.0
LongStream.range(1, 4).forEach(System.out::println); // 1 2 3
LongStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
} }

5

package com.gpcoder.stream.intro; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; public class PrimitiveStreamExample { public static void main(String[] args) {

IntStream.range(1, 4).forEach(System.out::println); // 1 2 3
IntStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
DoubleStream.of(1, 2, 3).forEach(System.out::println); // 1.0 2.0 3.0
LongStream.range(1, 4).forEach(System.out::println); // 1 2 3
LongStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
} }

6

Stream API với I/O

Như chúng tôi đã đề cập trước đây, Stream API không chỉ hỗ trợ các thao tác với các Collection, Array. Các hoạt động I/O trên tập tin như đọc file văn bản theo từng dòng (line) cũng có thể áp dụng với Stream API. Hãy xem ví dụ sau:

Chúng ta cần đọc những dòng dữ liệu có chữ bắt đầu là line3 của tập tin line.txt sau:

package com.gpcoder.stream.intro; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; public class PrimitiveStreamExample { public static void main(String[] args) {

IntStream.range(1, 4).forEach(System.out::println); // 1 2 3
IntStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
DoubleStream.of(1, 2, 3).forEach(System.out::println); // 1.0 2.0 3.0
LongStream.range(1, 4).forEach(System.out::println); // 1 2 3
LongStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
} }

7

Chương trình của chúng ta như sau:

package com.gpcoder.stream.intro; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; public class PrimitiveStreamExample { public static void main(String[] args) {

IntStream.range(1, 4).forEach(System.out::println); // 1 2 3
IntStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
DoubleStream.of(1, 2, 3).forEach(System.out::println); // 1.0 2.0 3.0
LongStream.range(1, 4).forEach(System.out::println); // 1 2 3
LongStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
} }

8

Phương thức onClose() trong đoạn code trên được gọi khi phương thức close() được gọi trên Stream.

Luồng song song – Parallel Streams

Như đã đề cập ở trên, các stream có thể là tuần tự (sequential) hoặc song song (parallel). Các thao tác trên các stream tuần tự được thực hiện trên một luồng đơn (single thread) trong khi các phép toán trên các stream song song được thực hiện đồng thời trên nhiều luồng (multi-thread).

So sánh thao tác operajon và phương thức method năm 2024

Cách hoạt động của Parallel Streams:

So sánh thao tác operajon và phương thức method năm 2024

Chúng ta thường sử dụng Parallel Streams trong môi trường multi-thread khi mà chúng ta cần hiệu suất xử lý nhanh.

Hãy xem ví dụ sau:đầu tiên chúng ta sẽ tạo một danh sách dữ liệu lớn, sau đó chúng ta sẽ sắp xếp danh sách này sử dụng stream() và parallelStream().

Sequential Stream

package com.gpcoder.stream.intro; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; public class PrimitiveStreamExample { public static void main(String[] args) {

IntStream.range(1, 4).forEach(System.out::println); // 1 2 3
IntStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
DoubleStream.of(1, 2, 3).forEach(System.out::println); // 1.0 2.0 3.0
LongStream.range(1, 4).forEach(System.out::println); // 1 2 3
LongStream.of(1, 2, 3).forEach(System.out::println); // 1 2 3
} }

9

Parallel Stream

package com.gpcoder.stream.convert; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; import java.util.stream.Stream; public class ConvertToStreamExample { // Generate Streams from Arrays using .stream or Stream.of public static void streamFromArray() {

String[] languages = { "Java", "C#", "C++", "PHP", "Javascript" };
// Get Stream using the Arrays.stream
Stream testStream1 = Arrays.stream(languages);
testStream1.forEach(x -> System.out.println(x));
// Get Stream using the Stream.of
Stream testStream2 = Stream.of(languages);
testStream2.forEach(x -> System.out.println(x));
} // Generate Streams from Collections public static void streamFromCollection() {
List items = new ArrayList<>();
items.add("Java");
items.add("C#");
items.add("C++");
items.add("PHP");
items.add("Javascript");
items.stream().forEach(item -> System.out.println(item));
} // Generate Streams using Stream.generate() public static void streamUsingGenerate() {
Stream stream = Stream.generate(() -> "gpcoder").limit(3);
String[] testStrArr = stream.toArray(String[]::new);
System.out.println(Arrays.toString(testStrArr)); // [gpcoder, gpcoder, gpcoder]
} // Generate Streams using Stream.iterate() public static void streamUsingIterate() {
Stream iterateNumbers = Stream.iterate(1L, n -> n + 1).limit(5);
iterateNumbers.forEach(System.out::print); // 12345
} // Generate Streams from APIs like Regex public static void streamUsingRegex() {
String str = "Welcome,to,gpcoder";
Pattern.compile(",").splitAsStream(str).forEach(System.out::print);// Welcometogpcoder
} }

0

Như bạn thấy cả hai đoạn code gần giống nhau nhưng sắp xếp song song nhanh hơn gần 50%. Tất cả những gì bạn phải làm là thay stream() thành parallelStream().

Hạn chế của Stream

Stream không thể tái sử dụng một khi đã gọi Terminal Operations:

package com.gpcoder.stream.convert; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; import java.util.stream.Stream; public class ConvertToStreamExample { // Generate Streams from Arrays using .stream or Stream.of public static void streamFromArray() {

String[] languages = { "Java", "C#", "C++", "PHP", "Javascript" };
// Get Stream using the Arrays.stream
Stream testStream1 = Arrays.stream(languages);
testStream1.forEach(x -> System.out.println(x));
// Get Stream using the Stream.of
Stream testStream2 = Stream.of(languages);
testStream2.forEach(x -> System.out.println(x));
} // Generate Streams from Collections public static void streamFromCollection() {
List items = new ArrayList<>();
items.add("Java");
items.add("C#");
items.add("C++");
items.add("PHP");
items.add("Javascript");
items.stream().forEach(item -> System.out.println(item));
} // Generate Streams using Stream.generate() public static void streamUsingGenerate() {
Stream stream = Stream.generate(() -> "gpcoder").limit(3);
String[] testStrArr = stream.toArray(String[]::new);
System.out.println(Arrays.toString(testStrArr)); // [gpcoder, gpcoder, gpcoder]
} // Generate Streams using Stream.iterate() public static void streamUsingIterate() {
Stream iterateNumbers = Stream.iterate(1L, n -> n + 1).limit(5);
iterateNumbers.forEach(System.out::print); // 12345
} // Generate Streams from APIs like Regex public static void streamUsingRegex() {
String str = "Welcome,to,gpcoder";
Pattern.compile(",").splitAsStream(str).forEach(System.out::print);// Welcometogpcoder
} }

1

Thực thi chương trình trên, ta nhận được thông báo lỗi như sau:

package com.gpcoder.stream.convert; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; import java.util.stream.Stream; public class ConvertToStreamExample { // Generate Streams from Arrays using .stream or Stream.of public static void streamFromArray() {

String[] languages = { "Java", "C#", "C++", "PHP", "Javascript" };
// Get Stream using the Arrays.stream
Stream testStream1 = Arrays.stream(languages);
testStream1.forEach(x -> System.out.println(x));
// Get Stream using the Stream.of
Stream testStream2 = Stream.of(languages);
testStream2.forEach(x -> System.out.println(x));
} // Generate Streams from Collections public static void streamFromCollection() {
List items = new ArrayList<>();
items.add("Java");
items.add("C#");
items.add("C++");
items.add("PHP");
items.add("Javascript");
items.stream().forEach(item -> System.out.println(item));
} // Generate Streams using Stream.generate() public static void streamUsingGenerate() {
Stream stream = Stream.generate(() -> "gpcoder").limit(3);
String[] testStrArr = stream.toArray(String[]::new);
System.out.println(Arrays.toString(testStrArr)); // [gpcoder, gpcoder, gpcoder]
} // Generate Streams using Stream.iterate() public static void streamUsingIterate() {
Stream iterateNumbers = Stream.iterate(1L, n -> n + 1).limit(5);
iterateNumbers.forEach(System.out::print); // 12345
} // Generate Streams from APIs like Regex public static void streamUsingRegex() {
String str = "Welcome,to,gpcoder";
Pattern.compile(",").splitAsStream(str).forEach(System.out::print);// Welcometogpcoder
} }

2

Để khắc phục hạn chế này, chúng ta phải tạo một Stream mới cho mọi hoạt động của thiết bị đầu cuối mà chúng ta muốn thực thi. Chương trình trên có thể viết lại như sau:

package com.gpcoder.stream.convert; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; import java.util.stream.Stream; public class ConvertToStreamExample { // Generate Streams from Arrays using .stream or Stream.of public static void streamFromArray() {

String[] languages = { "Java", "C#", "C++", "PHP", "Javascript" };
// Get Stream using the Arrays.stream
Stream testStream1 = Arrays.stream(languages);
testStream1.forEach(x -> System.out.println(x));
// Get Stream using the Stream.of
Stream testStream2 = Stream.of(languages);
testStream2.forEach(x -> System.out.println(x));
} // Generate Streams from Collections public static void streamFromCollection() {
List items = new ArrayList<>();
items.add("Java");
items.add("C#");
items.add("C++");
items.add("PHP");
items.add("Javascript");
items.stream().forEach(item -> System.out.println(item));
} // Generate Streams using Stream.generate() public static void streamUsingGenerate() {
Stream stream = Stream.generate(() -> "gpcoder").limit(3);
String[] testStrArr = stream.toArray(String[]::new);
System.out.println(Arrays.toString(testStrArr)); // [gpcoder, gpcoder, gpcoder]
} // Generate Streams using Stream.iterate() public static void streamUsingIterate() {
Stream iterateNumbers = Stream.iterate(1L, n -> n + 1).limit(5);
iterateNumbers.forEach(System.out::print); // 12345
} // Generate Streams from APIs like Regex public static void streamUsingRegex() {
String str = "Welcome,to,gpcoder";
Pattern.compile(",").splitAsStream(str).forEach(System.out::print);// Welcometogpcoder
} }

3

Trên đây là bài viết giới thiệu về Stream API trong Java 8 và một số phương thức phổ biến nhất để các bạn cùng tham khảo. Nếu như bạn muốn tìm hiểu các phương thức khác để làm việc hiệu quả thông qua Stream có thể tham khảo trang chủ của Oracle. Hy vọng bài viết này giúp ích cho các bạn. Cám ơn các bạn đã quan tâm và theo dõi bài viết. Hẹn gặp lại ở bài viết tiếp theo.