1. Java Stream
private static int sumIterator(List<Integer> list) {
Iterator<Integer>
it = list.iterator();
int sum = 0;
while (it.hasNext()) {
int num = it.next();
if (num > 10) {
sum += num;
}
}
return sum;
}
Có ba vấn đề chính với cách tiếp cận trên:
· Chúng tôi chỉ muốn biết tổng các số nguyên nhưng chúng tôi cũng sẽ phải cung cấp cách lặp sẽ diễn ra, điều này còn được gọi là external iteration (lặp bên ngoài) vì chương trình khách đang xử lý thuật toán để lặp qua danh sách.
· Chương trình có tính chất tuần tự, không có cách nào chúng ta có thể thực hiện song song việc này một cách dễ dàng.
· Có rất nhiều mã để thực hiện ngay cả một nhiệm vụ đơn giản.
Internal iteration (lặp lại nội bộ), cung cấp một số tính năng như thực thi tuần tự và song song, lọc dựa trên các tiêu chí đã cho, ánh xạ, v.v.
Hầu hết các đối số của phương thức Java 8 Stream API là các Functional Interface, vì vậy các biểu thức lambda hoạt động rất tốt với chúng. Hãy xem làm thế nào chúng ta có thể viết logic ở trên trong một câu lệnh dòng đơn bằng cách sử dụng Java Streams.
private static int sumStream(List<Integer> list) {
return list.stream().filter(i -> i > 10).mapToInt(i -> i).sum();
}
Trước hết, chúng ta sẽ xem xét các khái niệm cốt lõi của Java 8 Stream API và sau đó chúng ta sẽ đi qua một số ví dụ để hiểu các phương thức thường được sử dụng nhất.
2. Collections và Java Stream
Collections là một cấu trúc dữ liệu trong bộ nhớ để lưu giữ các giá trị và trước khi chúng tôi bắt đầu sử dụng Collection, tất cả các giá trị phải được điền. Trong khi java Stream là một cấu trúc dữ liệu được tính toán theo yêu cầu.Java Stream không lưu trữ dữ liệu, nó hoạt động trên cấu trúc dữ liệu nguồn (collection và array) và tạo ra dữ liệu tổng hợp mà chúng ta có thể sử dụng và thực hiện các hoạt động cụ thể. Chẳng hạn như chúng ta có thể tạo một Stream từ danh sách và lọc nó dựa trên một điều kiện.
Các hoạt động của Java Stream sử dụng các Functional interface, điều này làm cho nó rất phù hợp cho việc lập trình chức năng sử dụng biểu thức lambda. Như bạn có thể thấy trong ví dụ trên, việc sử dụng các biểu thức lambda làm cho mã của chúng ta dễ đọc và ngắn gọn.
Nguyên tắc lặp nội bộ của Java 8 Stream giúp đạt được tính năng tìm kiếm lười biếng trong một số hoạt động của Stream. Ví dụ: lọc, ánh xạ hoặc loại bỏ trùng lặp có thể được thực hiện một cách lười biếng, cho phép hiệu suất và phạm vi tối ưu hóa cao hơn.
Java Streams là có thể sử dụng được, vì vậy không có cách nào để tạo một tham chiếu đến stream để sử dụng trong tương lai. Vì dữ liệu theo yêu cầu nên không thể sử dụng lại cùng một luồng nhiều lần.
Java 8 Stream hỗ trợ tuần tự cũng như xử lý song song, xử lý song song có thể rất hữu ích trong việc đạt được hiệu suất cao cho các bộ sưu tập lớn.
Tất cả các class và interface Java Stream API đều nằm trong gói java.util.stream. Vì chúng ta có thể sử dụng các kiểu dữ liệu nguyên thủy như int, long trong bộ sưu tập sử dụng auto-boxing và các hoạt động này có thể mất rất nhiều thời gian, có những lớp học đặc biệt với nhiều loại nguyên thủy - IntStream, LongStream và DoubleStream.
3. Functional interfaces trong Java 8 Stream
Một số Functional interface thường được sử dụng trong các phương thức Stream API trong Java 8 là:1. Function và BiFunction: Function đại diện cho một hàm nhận một loại đối số và trả về một loại đối số khác. Function<T, R> là dạng tổng quát trong đó T là kiểu đầu vào của hàm và R là kiểu kết quả của hàm.
Để xử lý các loại nguyên thủy, có rất cụ thể các Functional interface - ToIntFunction, ToLongFunction, ToDoubleFunction, ToIntBiFunction, ToLongBiFunction, ToDoubleBiFunction, LongToIntFunction, LongToDoubleFunction, IntToLongFunction, IntToDoubleFunction, vv
Một số phương thức Stream nơi Function hoặc sự chuyên môn hóa ban đầu của nó được sử dụng là:
Ø IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper)- tương tự đối với long và double
Ø <A> A[] toArray(IntFunction<A[]> generator)
Ø <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)
2. Predicate and BiPredicate: Nó đại diện cho một vị từ dựa vào các phần tử của Stream được kiểm tra. Điều này được sử dụng để lọc các phần tử khỏi Java Stream. Cũng giống như Function, có các interface cụ thể nguyên thủy cho int, long và double.
Một số phương pháp Stream nơi mà Predicate hoặc BiPredicate chuyên môn được sử dụng là:
Ø Stream<T> filter(Predicate<? super T> predicate)
Ø boolean anyMatch(Predicate<? super T> predicate)
Ø boolean allMatch(Predicate<? super T> predicate)
Ø boolean noneMatch(Predicate<? super T> predicate)
3. Consumer và BiConsumer: Nó đại diện cho một hoạt động chấp nhận một đối số đầu vào duy nhất và không trả về kết quả nào. Nó có thể được sử dụng để thực hiện một số hành động trên tất cả các phần tử của java Stream.
Một số phương thức Java 8 Stream mà Consumer, BiConsumer hoặc các interface chuyên môn hóa ban đầu được sử dụng là:
Ø Stream<T> peek(Consumer<? super T> action)
Ø void forEach(Consumer<? super T> action)
Ø void forEachOrdered(Consumer<? super T> action)
4. Supplier: Supplier đại diện cho một hoạt động mà qua đó chúng tôi có thể tạo ra các giá trị mới trong Stream. Một số phương thức trong Stream có đối số Supplier là:
Ø public static<T> Stream<T> generate(Supplier<T> s)
Ø <R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner)
4. Java Optional java.util.Optional
Java Optional là một đối tượng vùng chứa có thể chứa hoặc không thể chứa giá trị khác rỗng. Nếu có giá trị, isPresent() sẽ trả về true và get() sẽ trả về giá trị. Các hoạt động đầu cuối Stream trả về đối tượng Optional. Một số phương pháp này là:Ø Optional<T> reduce(BinaryOperator<T> accumulator)
Ø Optional<T> min(Comparator<? super T> comparator)
Ø Optional<T> max(Comparator<? super T> comparator)
Ø Optional<T> findFirst()
Ø Optional<T> findAny()
5.
Spliterator - java.util.Spliterator
Để hỗ trợ thực thi song song trong Java 8 Stream API, Spliterator interface được sử dụng phương thức trySplit - Spliterator trả về một Spliterator mới quản lý một tập hợp con các phần tử của Spliterator ban đầu.6. Java Stream Intermediate và Terminal Operations
Các hoạt động Java Stream API trả về một Stream mới được gọi là intermediate operations (các hoạt động trung gian). Hầu hết các trường hợp, các hoạt động này có bản chất lười biếng, vì vậy chúng bắt đầu tạo ra các phần tử Stream mới và gửi nó đến hoạt động tiếp theo. Intermediate operations (các hoạt động trung gian) không bao giờ là hoạt động tạo ra kết quả cuối cùng. Các phép toán trung gian thường được sử dụng là filter và map.Các hoạt động Java8 Stream API trả về một kết quả hoặc tạo ra một tác dụng phụ. Khi phương thức đầu cuối được gọi trên một luồng, nó sẽ sử dụng luồng và sau đó chúng ta không thể sử dụng luồng. Terminal operations (Các hoạt động đầu cuối) là háo hức về bản chất, tức là chúng xử lý tất cả các phần tử trong luồng trước khi trả về kết quả. Thường được sử dụng phương pháp thiết bị đầu cuối được forEach, toArray, min, max, findFirst, anyMatch, allMatch vv. Bạn có thể xác định các phương pháp thiết bị đầu cuối từ các kiểu trả về, họ sẽ không bao giờ quay trở lại một Stream.
7. Java Stream Short Circuiting Operations (Các hoạt động tuần hoàn ngắn trong dòng Java)
Intermediate operation (Hoạt động trung gian) được gọi là short circuiting (đoản mạch) nếu nó có thể tạo ra dòng hữu hạn cho một dòng vô hạn. Ví dụ limit() và skip() là hai hoạt động trung gian ngắn mạch.Terminal operation được gọi là short circuiting nếu nó có thể kết thúc trong thời gian hữu hạn đối với dòng vô hạn. Ví dụ anyMatch, allMatch, noneMatch, findFirst và findAny là short circuiting terminal operations.
8. Ví dụ về Java Stream
Tôi đã trình bày gần như tất cả các phần quan trọng của Java 8 Stream API. Thật thú vị khi sử dụng các tính năng API mới này và chúng ta hãy xem nó hoạt động với một số ví dụ về java Stream.Có một số cách mà chúng ta có thể tạo một Stream trong java từ array và collection. Hãy xem xét những điều này với các ví dụ đơn giản.
Ø Chúng tôi có thể sử dụng Stream.of() để tạo Stream từ loại dữ liệu tương tự. Ví dụ, chúng ta có thể tạo Java Stream các số nguyên từ một nhóm các đối tượng int hoặc Integer.
Stream<Integer> stream = Stream.of(1,2,3,4);
Ø Chúng ta có thể sử dụng Stream.of() với một mảng Object để trả về Stream. Lưu ý rằng nó không hỗ trợ autoboxing, vì vậy chúng tôi không thể chuyển mảng kiểu nguyên thủy.Stream<Integer> stream = Stream.of(new Integer[]{1,2,3,4});
//works
fine
Stream<Integer>
stream1 = Stream.of(new int[]{1,2,3,4});
// Compile time error, Type mismatch: cannot convert from Stream<int[]> to Stream<Integer>
Ø Chúng ta có thể sử dụng Collection stream() để tạo luồng tuần tự và parallelStream() để tạo luồng song song.List<Integer> myList = new
ArrayList<>();
for (int
i = 0; i
< 100; i++)
myList.add(i);
// sequential stream
Stream<Integer> sequentialStream = myList.stream();
// parallel stream
Stream<Integer> parallelStream = myList.parallelStream();
Ø Chúng ta có thể sử dụng các phương pháp Stream.generate() và Stream.iterate() để tạo Stream.Stream<String> stream1 = Stream.generate(() -> {return "abc";});
Stream<String> stream2 = Stream.iterate("abc", (i) -> i);
Ø Sử dụng phương pháp Arrays.stream() và String.chars().LongStream is
= Arrays.stream(new long[]{1,2,3,4});
IntStream is2
= "abc".chars();
Chuyển đổi Java Stream thành
Collection hoặc Array
Có một số cách để chúng ta có thể lấy Collection hoặc Array từ java Stream.Ø Chúng ta có thể sử dụng phương thức collect() của java Stream để lấy List, Map hoặc Set từ stream.
Stream<Integer> intStream = Stream.of(1, 2, 3, 4);
List<Integer> intList = intStream.collect(Collectors.toList());
System.out.println(intList); // prints
[1, 2, 3, 4]
intStream = Stream.of(1, 2, 3, 4); // stream is closed, so we need to create it again
Map<Integer, Integer> intMap
= intStream.collect(Collectors.toMap(i
-> i, i -> i + 10));
System.out.println(intMap); // prints {1=11, 2=12, 3=13, 4=14}
Ø Chúng ta có thể sử dụng phương thức Stream: toArray() để tạo một Array từ Stream.Stream<Integer> intStream = Stream.of(1, 2, 3, 4);
Integer[] intArray
= intStream.toArray(Integer[]::new);
System.out.println(Arrays.toString(intArray)); // prints
[1, 2, 3, 4]
1. Ví dụ về Stream filter(): Chúng ta có thể sử dụng phương thức filter() để kiểm tra các phần tử của Stream cho một điều kiện và tạo danh sách đã lọc.
// Stream filter()
List<Integer> myList = new ArrayList<>();
for (int i = 0; i <
100; i++)
myList.add(i);
Stream<Integer> sequentialStream = myList.stream();
Stream<Integer> highNums = sequentialStream.filter(p -> p > 90);
// filter numbers greater than 90
System.out.print("High Nums greater than
90=");
highNums.forEach(p -> System.out.print(p + "
"));
// prints "High Nums greater than 90=91 92 93 94 95 96 97 98 99 "
// +++++++++++++Stream map()
Stream<String> names = Stream.of("aBc", "d", "ef");
System.out.println(names.map(s -> {
return s.toUpperCase();
}).collect(Collectors.toList()));// prints [ABC, D, EF]
Stream<String> names2 = Stream.of("aBc", "d", "ef", "123456");
List<String> reverseSorted = names2.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
System.out.println(reverseSorted); // [ef, d, aBc, 123456]
Stream<String> names3 = Stream.of("aBc", "d", "ef", "123456");
List<String> naturalSorted = names3.sorted().collect(Collectors.toList());
System.out.println(naturalSorted); // [123456, aBc, d, ef]
Stream<List<String>> namesOriginalList = Stream.of(
Arrays.asList("Pankaj"),
Arrays.asList("David", "Lisa"),
Arrays.asList("Amit"));
// flat the stream from List<String> to String
stream
Stream<String> flatStream = namesOriginalList.flatMap(
strList -> strList.stream());
flatStream.forEach(System.out::println);
1. Ví dụ về Stream reduce() : Chúng ta có thể sử dụng reduce() để thực hiện giảm các phần tử của Stream, sử dụng hàm tích lũy liên kết và trả về Optional. Hãy xem cách chúng ta có thể sử dụng nó để nhân các số nguyên trong một Stream.
//Stream reduce
Stream<Integer> numbers = Stream.of(1,2,3,4,5);
Optional<Integer>
intOptional = numbers.reduce((i,j) -> {return i*j;});
if(intOptional.isPresent())
System.out.println("Multiplication = "+intOptional.get()); //120
/// Stream count
Stream<Integer> numbers1 = Stream.of(1, 2, 3, 4, 5);
System.out.println("Number of elements in stream=" + numbers1.count()); // 5
// Stream foreach()
Stream<Integer> numbers2 = Stream.of(1, 2, 3, 4, 5);
numbers2.forEach(i -> System.out.print(i + ",")); // 1,2,3,4,5,
// Stream foreach()
Stream<Integer> numbers2 = Stream.of(1, 2, 3, 4, 5);
numbers2.forEach(i -> System.out.print(i + ",")); // 1,2,3,4,5,
// Stream Match()
Stream<Integer> numbers3 = Stream.of(1, 2, 3, 4, 5);
System.out.println("Stream contains 4? " + numbers3.anyMatch(i -> i == 4));
// Stream contains 4? true
Stream<Integer> numbers4 = Stream.of(1, 2, 3, 4, 5);
System.out.println("Stream contains all elements
less than 10? "
+ numbers4.allMatch(i -> i < 10));
// Stream contains all elements less than 10? true
Stream<Integer> numbers5 = Stream.of(1, 2, 3, 4, 5);
System.out.println("Stream doesn't contain 10?
"
+ numbers5.noneMatch(i -> i == 10));
// Stream doesn't contain 10? true
// Stream findFirst
Stream<String> names4 = Stream.of("Pankaj", "Amit", "David", "Lisa");
Optional<String> firstNameWithD = names4.filter(
i -> i.startsWith("D")).findFirst();
if (firstNameWithD.isPresent()) {
System.out.println("First Name starting with D="
+ firstNameWithD.get()); // David
}
9. Hạn chế của Stream API Java 8
Java 8 Stream API mang đến rất nhiều thứ mới để làm việc với danh sách và mảng, nhưng nó cũng có một số hạn chế.StatefulParallelStream.java
package com.codetimes90.Java8.StreamAndLambda;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class Stream_StatefulParallelStream {
public static void main(String[] args) {
List<Integer>
ss = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
List<Integer>
result = new ArrayList<Integer>();
Stream<Integer>
stream = ss.parallelStream();
stream.map(s ->{ synchronized (result) {
if(result.size() < 10) {
result.add(s);
}
}
return s;
}).forEach(e -> {});
System.out.println(result);
}
}
2. Sau khi một Stream được sử dụng, không thể sử dụng luồng đó sau này. Như bạn có thể thấy trong các ví dụ trên, mỗi khi tôi tạo Stream.
3. Có rất nhiều phương thức trong Stream API và phần khó hiểu nhất là các phương thức được nạp chồng. Nó làm cho đường cong học tập mất thời gian.
Đó là tất cả cho hướng dẫn ví dụ về Java 8 Stream. Tôi mong muốn sử dụng tính năng này và làm cho mã có thể đọc được với hiệu suất tốt hơn thông qua xử lý song song.
0 Nhận xét