Skip to main content

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

  1. 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());
  2. 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(...)
  3. 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

  1. 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());
  2. 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());
  3. Quên Xử Lý Null

    // Lỗi
    stream.map(String::toUpperCase) // NullPointerException nếu có null

    // Đúng
    stream.filter(Objects::nonNull)
    .map(String::toUpperCase)