API Streams trong Java
Stream API được giới thiệu trong Java 8, cung cấp một cách tiếp cận mới để xử lý dữ liệu theo kiểu khai báo (declarative). Nó cho phép thực hiện các thao tác phức tạp trên tập hợp dữ liệu một cách hiệu quả và dễ đọc.
1. Tạo Stream
Từ Collection
List<String> list = Arrays.asList("Java", "Python", "JavaScript");
Stream<String> stream = list.stream();
Stream<String> parallelStream = list.parallelStream();
Từ Mảng
String[] array = {"Java", "Python", "JavaScript"};
Stream<String> stream = Arrays.stream(array);
Từ Giá Trị
Stream<String> stream = Stream.of("Java", "Python", "JavaScript");
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5);
Stream Vô Hạn
// Stream số tự nhiên vô hạn
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 1);
// Stream số ngẫu nhiên vô hạn
Stream<Double> randomStream = Stream.generate(Math::random);
2. Các Thao Tác Trung Gian (Intermediate Operations)
Filter
List<String> languages = Arrays.asList("Java", "Python", "JavaScript", "Ruby");
List<String> filtered = languages.stream()
.filter(lang -> lang.startsWith("J"))
.collect(Collectors.toList()); // ["Java", "JavaScript"]
Map
List<String> names = Arrays.asList("john", "jane", "jack");
List<String> upperNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList()); // ["JOHN", "JANE", "JACK"]
FlatMap
List<List<String>> lists = Arrays.asList(
Arrays.asList("Java", "Python"),
Arrays.asList("JavaScript", "Ruby")
);
List<String> flattened = lists.stream()
.flatMap(List::stream)
.collect(Collectors.toList()); // ["Java", "Python", "JavaScript", "Ruby"]
Sorted
List<String> languages = Arrays.asList("Java", "Python", "JavaScript");
List<String> sorted = languages.stream()
.sorted()
.collect(Collectors.toList()); // ["Java", "JavaScript", "Python"]
Distinct
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 4);
List<Integer> unique = numbers.stream()
.distinct()
.collect(Collectors.toList()); // [1, 2, 3, 4]
3. Các Thao Tác Kết Thúc (Terminal Operations)
Collect
List<String> languages = Arrays.asList("Java", "Python", "JavaScript");
// Thu thập thành List
List<String> list = languages.stream()
.collect(Collectors.toList());
// Thu thập thành Set
Set<String> set = languages.stream()
.collect(Collectors.toSet());
// Thu thập thành Map
Map<String, Integer> map = languages.stream()
.collect(Collectors.toMap(
Function.identity(),
String::length
));
Reduce
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Tính tổng
int sum = numbers.stream()
.reduce(0, Integer::sum); // 15
// Tìm giá trị lớn nhất
int max = numbers.stream()
.reduce(Integer.MIN_VALUE, Integer::max); // 5
ForEach
List<String> languages = Arrays.asList("Java", "Python", "JavaScript");
languages.stream()
.forEach(System.out::println);
Count
List<String> languages = Arrays.asList("Java", "Python", "JavaScript");
long count = languages.stream()
.filter(lang -> lang.startsWith("J"))
.count(); // 2
4. Ví Dụ Thực Tế
Xử Lý Dữ Liệu Sản Phẩm
public class Product {
private String name;
private double price;
private String category;
// Constructor và getter/setter
}
public class ProductProcessor {
private List<Product> products;
public double getAveragePriceByCategory(String category) {
return products.stream()
.filter(p -> p.getCategory().equals(category))
.mapToDouble(Product::getPrice)
.average()
.orElse(0.0);
}
public List<Product> getExpensiveProducts(double threshold) {
return products.stream()
.filter(p -> p.getPrice() > threshold)
.sorted(Comparator.comparing(Product::getPrice).reversed())
.collect(Collectors.toList());
}
public Map<String, List<Product>> groupByCategory() {
return products.stream()
.collect(Collectors.groupingBy(Product::getCategory));
}
}
Xử Lý Đơn Hàng
public class Order {
private String orderId;
private List<OrderItem> items;
private LocalDateTime orderDate;
// Constructor và getter/setter
}
public class OrderProcessor {
private List<Order> orders;
public double getTotalRevenue() {
return orders.stream()
.flatMap(order -> order.getItems().stream())
.mapToDouble(item -> item.getPrice() * item.getQuantity())
.sum();
}
public List<Order> getRecentOrders(int days) {
LocalDateTime cutoff = LocalDateTime.now().minusDays(days);
return orders.stream()
.filter(order -> order.getOrderDate().isAfter(cutoff))
.sorted(Comparator.comparing(Order::getOrderDate).reversed())
.collect(Collectors.toList());
}
}
5. Best Practices
-
Sử Dụng Stream Thay Vì Vòng Lặp
// Không nên
List<String> result = new ArrayList<>();
for (String item : list) {
if (item.startsWith("J")) {
result.add(item.toUpperCase());
}
}
// Nên
List<String> result = list.stream()
.filter(item -> item.startsWith("J"))
.map(String::toUpperCase)
.collect(Collectors.toList()); -
Tối Ưu Hóa Chuỗi Thao Tác
// Không nên
stream.filter(...).map(...).filter(...).map(...)
// Nên
stream.filter(...)
.map(...)
.filter(...)
.map(...) -
Sử Dụng Parallel Stream Khi Cần
// Khi cần xử lý song song
List<String> result = list.parallelStream()
.filter(...)
.map(...)
.collect(Collectors.toList());
6. Lỗi Thường Gặp
-
Quên Thu Thập Kết Quả
// Lỗi
stream.filter(...).map(...); // Không có terminal operation
// Đúng
stream.filter(...)
.map(...)
.collect(Collectors.toList()); -
Sử Dụng Stream Đã Đóng
// Lỗi
Stream<String> stream = list.stream();
stream.collect(Collectors.toList());
stream.collect(Collectors.toList()); // IllegalStateException
// Đúng
list.stream().collect(Collectors.toList());
list.stream().collect(Collectors.toList()); -
Quên Xử Lý Null
// Lỗi
stream.map(String::toUpperCase) // NullPointerException nếu có null
// Đúng
stream.filter(Objects::nonNull)
.map(String::toUpperCase)