Stream API là gì? Stream API trong Java 8
NỘI DUNG BÀI VIẾT
1. Stream API là gì?
Stream là 1 lớp trừu tượng mới được giới thiệu trong Java 8. Sử dụng Stream, bạn có thể xử lý dữ liệu 1 cách tự nhiên giống như các câu lệnh SQL. Ví dụ ta có câu SQL sau:
SELECT SUM(salary) FROM Employee
Câu lệnh trên tự động trả về tổng lương của tất cả Employee mà không cần phải thực hiện bất kì tính toán gì ở phía đầu cuối developer (Bình thường thì sẽ SELECT tất cả các Employeesau đó dùng code Java để duyệt và tính tổng của tất cả Employee).
Tương tự vấn đề trên, khi sử dụng Collections trong Java, chúng ta thực hiện các vòng lặp và thực hiện lại các đoạn kiểm tra. Giả sử muốn tính tổng lương của các employee có role = ‘Developer’ từ 1 danh sách chúng ta phải thực hiện lặp tất cả các phần tử, kiểm tra phần tử đó có role = ‘Developer’ rồi cộng lại. Trong khi nếu muốn xử lý chúng song song lại dễ gặp lỗi.
public static final String ROLE_DEVELOPER = "Developer";
public double sumSalaryOfDeveloper(List<Employee> listEmployee) {
double sumSalary = 0;
for (Employee emp : listEmployee) {
if (emp.getRole().equals(ROLE_DEVELOPER)) {
sumSalary += emp.getSalary();
}
}
return sumSalary;
}
Code language: PHP (php)
Để giải quyết vấn đề đó, Java 8 giới thiệu Stream API giúp developer xử lý dữ liệu khai báo và tận dụng kiến trúc đa lõi (multicore) mà không cần viết mã cụ thể cho nó.
public static final String ROLE_DEVELOPER = "Developer";
public double sumSalaryOfDeveloper(List<Employee> listEmployee) {
return listEmployee.stream().filter(p -> p.getRole().equals(ROLE_DEVELOPER)).mapToDouble(p -> p.getSalary()).sum();
}
Code language: PHP (php)
Như vậy, bạn có thể hiểu stream đại diện cho một collection được xử lý tuần tự và hỗ trợ rất nhiều loại operation để tính toán dựa trên những element của collection đó (tính tổng, convert sang map, …)
2. Cách tạo 1 Stream
2.1 Tạo 1 empty stream
Method empty() được sử dụng để tạo 1 empty stream:
Stream<String> streamEmpty = Stream.empty();
Code language: HTML, XML (xml)
empty stream thường được dùng khi khởi tạo để tránh việc trả về null cho các stream không có phần tử nào:
public Stream<String> streamOf(List<String> list) {
return list == null || list.isEmpty() ? Stream.empty() : list.stream();
}
Code language: PHP (php)
2.2 Tạo stream từ collection (Stream of Collection)
Stream cũng có thể được tạo từ bất kì kiểu collection nào (Collection, List, Set):
Collection<String> collection = Arrays.asList("a", "b", "c");
Stream<String> streamOfCollection = collection.stream();
Code language: JavaScript (javascript)
2.3 Stream of Array
Stream cũng có thể được tạo từ 1 array hoặc 1 phần của array:
Stream<String> streamOfArray = Stream.of("a", "b", "c");
Code language: JavaScript (javascript)
String[] arr = new String[]{"a", "b", "c"};
Stream<String> streamOfArrayFull = Arrays.stream(arr);
Stream<String> streamOfArrayPart = Arrays.stream(arr, 1, 3);
Code language: JavaScript (javascript)
2.4 Stream.builder()
Stream.builder() được sử dụng khi muốn thêm mới 1 phần vào bên phải stream.
Stream<String> streamBuilder =
Stream.<String>builder().add("a").add("b").add("c").build();
Code language: JavaScript (javascript)
2.5 Stream.generate()
Phương thức generate() chấp nhận một Supplier<T> cho các phần tử được tạo ra. Số phần tử được tạo ra là vô hạn nên cần phải dùng lệnh .limit() để giới hạn số phần tử được tạo ra.
Ví dụ đoạn code dưới đây sẽ tạo ra liên tục 10 String có giá trị là “stackjava”
Stream<String> streamGenerated =
Stream.generate(() -> "stackjava").limit(10);
Code language: JavaScript (javascript)
2.6 Stream.iterate()
1 cách khác để tạo ra 1 stream vô hạn là sử dụng method iterate():
Stream<String> streamIterated = Stream.iterate("StackJava", n -> n + 1).limit(5);
List<String> listIterated = streamIterated.collect(Collectors.toList());
System.out.println(listIterated);
Code language: JavaScript (javascript)
Kết quả:
[StackJava, StackJava1, StackJava11, StackJava111, StackJava1111]
Code language: JSON / JSON with Comments (json)
2.7 Stream of Primitives
IntStream intStream = IntStream.range(1, 3);
2.8 Stream of String
Stream<String> streamOfString =
Pattern.compile(", ").splitAsStream("a, b, c");
Code language: JavaScript (javascript)
2.9. Stream of File
Path path = Paths.get("C:\\file.txt");
Stream<String> streamOfStrings = Files.lines(path);
Stream<String> streamWithCharset = Files.lines(path, Charset.forName("UTF-8"));
Code language: JavaScript (javascript)
3. Các ví dụ, method thường dùng với Stream
3.1 Chuyển 1 stream sang Collection hoặc array:
- Chúng ta có thể dùng stream
collect()
để tạo 1 List, Set, Map 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 bị đóng nên cần khởi tạo lại (*đọc phần lưu ý cuối bài)
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}
Code language: JavaScript (javascript)
- Dùng stream toArray() để tạo 1 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]
Code language: JavaScript (javascript)
3.2 Stream.filter()
Stream.filter() Trả về 1 stream với các phần tử khớp với Predicate.
Predicate<Integer> p = num -> num % 2 == 0;
List<Integer> list = Arrays.asList(3,4,6);
list.stream().filter(p).forEach(e -> System.out.print(e+" "));
Code language: PHP (php)
Kết quả:
4 6
3.3 Stream.allMatch(), Stream.anyMatch() and Stream.noneMatch()
allMatch()
: trả về true nếu tất cả các phần tử khớp với Predicate
.anyMatch()
: trả về true nếu có ít nhất 1 phần tử khớp với Predicate
.noneMatch()
: trả về true nếu không có phần tử nào khớp với Predicate
.
Ví du:
Predicate<Integer> p = num -> num % 2 == 0;
List<Integer> list = Arrays.asList(3,5,6);
System.out.println("allMatch:" + list.stream().allMatch(p));
System.out.println("anyMatch:" + list.stream().anyMatch(p));
System.out.println("noneMatch:" + list.stream().noneMatch(p));
Code language: PHP (php)
Kết quả:
allMatch:false
anyMatch:true
noneMatch:false
Code language: JavaScript (javascript)
3.4 Stream.findAny() and Stream.findFirst()
findAny()
: Trả về 1 phần tử bất kì của stream.findFirst()
: Trả về phần tử đầu tiên của stream.
Ví dụ:
List<String> list = Arrays.asList("G","B","F","E");
String any = list.stream().findAny().get();
System.out.println("FindAny: "+ any);
String first = list.stream().findFirst().get();
System.out.println("FindFirst: "+ first);
Code language: PHP (php)
Kết quả:
FindAny: G
FindFirst: G
Code language: HTTP (http)
3.5 Stream.distinct()
Stream.distinct() trả về 1 stream với các phần tử riêng biệt (không trùng nhau)
List<Integer> list = Arrays.asList(3,4,6,6,4);
System.out.print("Distinct elements: ");
list.stream().distinct().forEach(p -> System.out.print(p + ", "));
Code language: PHP (php)
Kết quả:
Distinct elements: 3, 4, 6,
3.6 Stream map()
Sử dụng map() để map (ánh xạ) mỗi phần tử của stream sang 1 giá trị tương ứng. Ví dụ chuyển các string của 1 list sang chữ hoa:
Stream<String> names = Stream.of("stack", "Java", "stackjava.com");
System.out.println(names.map(s -> {
return s.toUpperCase();
}).collect(Collectors.toList()));
//prints [STACK, JAVA, STACKJAVA.COM]
Code language: JavaScript (javascript)
3.7 Stream.max() and Stream.min()
max()
: tìm phần tử có giá trị lớn nhất dựa theo Comparator
.min()
: tìm phần tử có giá trị nhỏ nhất dựa theoComparator
.
Ví dụ:
List<String> list = Arrays.asList("G","B","F","E");
String max = list.stream().max(Comparator.comparing(String::valueOf)).get();
System.out.println("Max:"+ max);
String min = list.stream().min(Comparator.comparing(String::valueOf)).get();
System.out.println("Min:"+ min);
Code language: PHP (php)
Kết quả:
Max:G
Min:B
Code language: CSS (css)
List<String> list = Arrays.asList("A","B","C");
list.stream().peek(s->System.out.println(s+s)).collect(Collectors.toList());
Code language: PHP (php)
Kết quả:
AA
BB
CC
Trả về 1 new stream gồm tất cả các phần tử của stream sau khi áp dụng Consumer
.
Ví dụ:
* Một số lưu ý với Stream
Reuse với stream: stream không thể reuse lại:
Stream<String> names = Stream.of("stack", "java", "stackjava.com");
names.forEach(p -> System.out.print(p + " ")); // print: stack java stackjava.com
names.forEach(p -> System.out.print(p + " ")); // exception: Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
Code language: PHP (php)
Cách xử lý: khởi tạo lại stream hoặc dùng Supplier:
Stream<String> names = Stream.of("stack", "java", "stackjava.com");
names.forEach(p -> System.out.print(p + " ")); // print: stack java stackjava.com
// Khởi tạo lại stream
names = Stream.of("stack", "java", "stackjava.com");
names.forEach(p -> System.out.print(p + " ")); // print: stack java stackjava.com
// dùng supplier:
Supplier<Stream<String>> streamSupplier = () -> Stream.of("stack", "java", "stackjava.com");
streamSupplier.get().forEach(p -> System.out.print(p + " ")); // print: stack java stackjava.com
streamSupplier.get().forEach(p -> System.out.print(p + " ")); // print: stack java stackjava.com
Code language: PHP (php)
References:
https://stackjava.com/java8/stream-api-la-gi-stream-api-trong-java-8.html
Leave a Reply