스트림 수집기의 실제 예
21285 단어 apistreamfunctionaljava
Collectors
메서드는 대부분의 사용 사례에 적합합니다. Collection
또는 스칼라를 반환할 수 있습니다. 전자의 경우 toXXX()
방법 중 하나를 사용하고 후자의 경우 reducing()
방법 중 하나를 사용합니다.장바구니를 구현하는 전자 상거래 플랫폼을 상상해 봅시다. 카트는 다음과 같이 모델링됩니다.
이 다이어그램은 다음(축약된) 코드로 변환될 수 있습니다.
public class Product {
private final Long id; // 1
private final String label; // 1
private final BigDecimal price; // 1
public Product(Long id, String label, BigDecimal price) {
this.id = id;
this.label = label;
this.price = price;
}
@Override
public boolean equals(Object object ) { ... } // 2
@Override
public int hashCode() { ... } // 2
}
id
에만 의존public class Cart {
private final Map<Product, Integer> products = new HashMap<>(); // 1
public void add(Product product) {
add(product, 1);
}
public void add(Product product, int quantity) {
products.merge(product, quantity, Integer::sum);
}
public void remove(Product product) {
products.remove(product);
}
public void setQuantity(Product product, int quantity) {
products.put(product, quantity);
}
public Map<Product, Integer> getProducts() {
return Collections.unmodifiableMap(products); // 2
}
}
Product
입니다. 값은 수량입니다. 데이터를 메모리에 저장하는 방법을 정의했으면 카트를 화면에 표시하는 방법을 디자인해야 합니다. 결제 화면에는 두 가지 다른 정보가 표시되어야 합니다.
다음은 해당 코드입니다.
public record CartRow(Product product, int quantity) { // 1
public CartRow(Map.Entry<Product, Integer> entry) {
this(entry.getKey(), entry.getValue());
}
public BigDecimal getRowPrice() {
return product.getPrice().multiply(new BigDecimal(quantity));
}
}
CartRow
는 값 개체입니다.Java 16
record
으로 모델링할 수 있습니다.var rows = cart.getProducts()
.entrySet()
.stream()
.map(CartRow::new)
.collect(Collectors.toList()); // 1
var price = cart.getProducts()
.entrySet()
.stream()
.map(CartRow::new)
.map(CartRow::getRowPrice) // 2
.reduce(BigDecimal.ZERO, BigDecimal::add); // 3
Java 스트림의 주요 제한 사항 중 하나는 스트림을 한 번만 사용할 수 있다는 것입니다. 그 이유는 스트리밍 객체가 반드시 불변인 것은 아니기 때문입니다(불변할 수는 있지만). 따라서 동일한 스트림을 두 번 실행하는 것은 멱등성이 아닐 수 있습니다.
따라서 행과 가격을 모두 가져오려면 카트에서 두 개의 스트림을 생성해야 합니다. 한 스트림에서 행을 가져오고 다른 스트림에서 가격을 가져옵니다.
이것은 방법이 아닙니다.
단일 스트림에서 행과 가격을 모두 수집하려고 합니다. 단일 개체로 한 번에 둘 다 반환하는 사용자 정의
Collector
가 필요합니다.public class PriceAndRows {
private BigDecimal price; // 1
private final List<CartRow> rows = new ArrayList<>(); // 2
PriceAndRows(BigDecimal price, List<CartRow> rows) {
this.price = price;
this.rows.addAll(rows);
}
PriceAndRows() {
this(BigDecimal.ZERO, new ArrayList<>());
}
}
다음은
Collector
인터페이스에 대한 요약입니다. 자세한 내용은 this previous post을 확인하십시오.상호 작용
설명
supplier()
시작할 기본 개체를 제공합니다.accumulator()
현재 스트리밍된 항목을 컨테이너에 누적하는 방법 설명combiner()
스트림이 병렬인 경우 이를 병합하는 방법을 설명하십시오.finisher()
변경 가능한 컨테이너 유형이 반환된 유형이 아닌 경우 전자를 후자로 변환하는 방법을 설명하십시오.characteristics()
스트림 최적화를 위한 메타데이터 제공이를 감안할 때 그에 따라
Collector
를 구현할 수 있습니다.private class PriceAndRowsCollector
implements Collector<Map.Entry<Product, Integer>, PriceAndRows, PriceAndRows> {
@Override
public Supplier<PriceAndRows> supplier() {
return PriceAndRows::new; // 1
}
@Override
public BiConsumer<PriceAndRows, Map.Entry<Product, Integer>> accumulator() {
return (priceAndRows, entry) -> { // 2
var row = new CartRow(entry);
priceAndRows.price = priceAndRows.price.add(row.getRowPrice());
priceAndRows.rows.add(row);
};
}
@Override
public BinaryOperator<PriceAndRows> combiner() {
return (c1, c2) -> { // 3
c1.price = c1.price.add(c2.price);
var rows = new ArrayList<>(c1.rows);
rows.addAll(c2.rows);
return new PriceAndRows(c1.price, rows);
};
}
@Override
public Function<PriceAndRows, PriceAndRows> finisher() {
return Function.identity(); // 4
}
@Override
public Set<Characteristics> characteristics() {
return Set.of(Characteristics.IDENTITY_FINISH); // 4
}
}
PriceAndRows
의 인스턴스입니다. PriceAndRows
에 누적됩니다. PriceAndRows
는 총 가격을 합산하고 각각의 행을 집계하여 결합할 수 있습니다. Collector
를 설계하는 것은 약간 복잡하지만 사용자 지정 컬렉터를 사용하는 것은 다음과 같이 쉽습니다.var priceAndRows = cart.getProducts()
.entrySet()
.stream()
.collect(new PriceAndRowsCollector());
결론
Collectors
클래스에서 제공되는 즉시 사용 가능한 수집기 중 하나로 대부분의 사용 사례를 해결할 수 있습니다. 그러나 일부는 사용자 지정Collector
을 구현해야 합니다(예: 단일 컬렉션 또는 단일 스칼라 이상을 수집해야 하는 경우).이전에 개발한 적이 없다면 복잡해 보일 수 있지만 그렇지 않습니다. 약간의 연습만 하면 됩니다. 이 게시물이 도움이 되길 바랍니다.
Maven 형식의 GitHub에서 이 게시물의 소스 코드를 찾을 수 있습니다.
아자바긱 / 커스텀 컬렉터
더 나아가려면:
2021년 5월 2일 A Java Geek에서 원래 게시됨
Reference
이 문제에 관하여(스트림 수집기의 실제 예), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/nfrankel/a-real-world-example-of-a-stream-collector-b1b텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)