π Java Stream API νΈλ¬λΈμν κ°μ΄λ
Java 8 Stream APIλ₯Ό μ¬μ©νλ κ³Όμ μμ μμ£Ό λ°μνλ λ¬Έμ μ ν΄κ²° λ°©λ²μ μ 리νμ΅λλ€.
π λ¬Έμ 1: Stream μ½λ μ΄ν΄ μ΄λ €μ
π μλ¬ μν©
λ€μκ³Ό κ°μ Stream API μ½λλ₯Ό λ§λ¬μ λ λμ λ°©μμ μ΄ν΄νκΈ° μ΄λ €μ΄ μν©μ΄ λ°μν©λλ€.
List<Stock> newStocks = IntStream.range(0, request.getQuantity())
.mapToObj(i -> createStockEntity(product))
.collect(Collectors.toList());
π― μμΈ λΆμ
Stream APIλ ν¨μν νλ‘κ·Έλλ° ν¨λ¬λ€μμΌλ‘, κΈ°μ‘΄μ λͺ λ Ήν νλ‘κ·Έλλ°κ³Ό λ€λ₯Έ μ¬κ³ λ°©μμ΄ νμν©λλ€.
- λ°μ΄ν° νλ¦ λ°©μ: μ ν΅μ μΈ for λ°λ³΅λ¬Έκ³Ό λ¬λ¦¬ λ°μ΄ν°κ° νμ΄νλΌμΈμ ν΅ν΄ νλ₯΄λ λ°©μ
- 체μ΄λ λ©μλ: μ¬λ¬ λ©μλκ° μ°κ²°λμ΄ νλμ μμ μ μν
-
λλ€ ννμ:
i -> createStockEntity(product)
κ°μ μ΅λͺ ν¨μ μ¬μ©
π§ ν΄κ²° λ°©λ²
1λ¨κ³: Stream νμ΄νλΌμΈ λ¨κ³λ³ μ΄ν΄
// π λ¨κ³λ³ λΆμ
List<Stock> newStocks = IntStream.range(0, request.getQuantity()) // 1οΈβ£ μ«μ μ€νΈλ¦Ό μμ±
.mapToObj(i -> createStockEntity(product)) // 2οΈβ£ κ°μ²΄λ‘ λ³ν
.collect(Collectors.toList()); // 3οΈβ£ 리μ€νΈλ‘ μμ§
1οΈβ£ μ«μ μ€νΈλ¦Ό μμ±
IntStream.range(0, request.getQuantity())
// request.getQuantity()κ° 5λΌλ©΄: [0, 1, 2, 3, 4] μμ±
2οΈβ£ κ°μ²΄λ‘ λ³ν
.mapToObj(i -> createStockEntity(product))
// κ° μ«μ iμ λν΄ createStockEntity(product) μ€ν
// κ²°κ³Ό: [Stockκ°μ²΄1, Stockκ°μ²΄2, Stockκ°μ²΄3, Stockκ°μ²΄4, Stockκ°μ²΄5]
3οΈβ£ 리μ€νΈλ‘ μμ§
.collect(Collectors.toList())
// Streamμ List<Stock>μΌλ‘ λ³ν
2λ¨κ³: κΈ°μ‘΄ forλ¬Έκ³Ό λΉκ΅
μ ν΅μ μΈ λ°©μ
// β λͺ
λ Ήν νλ‘κ·Έλλ° - "μ΄λ»κ²" ν μ§λ₯Ό μ§μ
List<Stock> newStocks = new ArrayList<>();
for (int i = 0; i < request.getQuantity(); i++) {
Stock newStock = createStockEntity(product);
newStocks.add(newStock);
}
Stream API λ°©μ
// β
μ μΈν νλ‘κ·Έλλ° - "무μμ" ν μ§λ₯Ό μ μΈ
List<Stock> newStocks = IntStream.range(0, request.getQuantity())
.mapToObj(i -> createStockEntity(product))
.collect(Collectors.toList());
π Stream API ν΅μ¬ κ°λ
λ©μλ | μν | μμ |
---|---|---|
IntStream.range(start, end) |
μ μ λ²μ μ€νΈλ¦Ό μμ± |
IntStream.range(0, 5) β [0,1,2,3,4] |
.mapToObj() |
κ° μμλ₯Ό κ°μ²΄λ‘ λ³ν | i -> new Stock() |
.collect() |
μ΅μ’ κ²°κ³Όλ¬Όλ‘ μμ§ | Collectors.toList() |
π λ¬Έμ 2: Stream API μ±λ₯ μ€ν΄
π μλ¬ μν©
βStream APIκ° forλ¬Έλ³΄λ€ λ리λ€βλ μλͺ»λ μΈμμΌλ‘ μΈν΄ μ¬μ©μ κΈ°νΌνλ κ²½μ°κ° μμ΅λλ€.
π― μμΈ λΆμ
Stream APIμ μ±λ₯ νΉμ±μ μ λλ‘ μ΄ν΄νμ§ λͺ»ν κ²½μ°μ λλ€.
-
λ³λ ¬ μ²λ¦¬:
parallelStream()
μΌλ‘ λ©ν°μ½μ΄ νμ© κ°λ₯ - μ§μ° νκ°: νμν λκΉμ§ μ°μ°μ λ―Έλ£¨μ΄ μ΅μ ν
- λ©λͺ¨λ¦¬ ν¨μ¨μ±: μ€κ° 컬λ μ μμ± μμ΄ μ²λ¦¬
π§ ν΄κ²° λ°©λ²
μ±λ₯ λΉκ΅ μμ
@Service
@RequiredArgsConstructor
public class StockService {
// β
Stream API - κ°λ
μ±κ³Ό μ±λ₯ λͺ¨λ μ°μ
public List<Stock> createStocksStreamWay(Product product, int quantity) {
return IntStream.range(0, quantity)
.mapToObj(i -> createStockEntity(product))
.collect(Collectors.toList());
}
// β
λ³λ ¬ μ²λ¦¬λ‘ μ±λ₯ ν₯μ (λλ λ°μ΄ν° μ)
public List<Stock> createStocksParallel(Product product, int quantity) {
return IntStream.range(0, quantity)
.parallel() // π λ³λ ¬ μ²λ¦¬ νμ±ν
.mapToObj(i -> createStockEntity(product))
.collect(Collectors.toList());
}
// π μ ν΅μ μΈ λ°©μ
public List<Stock> createStocksTraditionalWay(Product product, int quantity) {
List<Stock> stocks = new ArrayList<>(quantity); // ν¬κΈ° 미리 ν λΉ
for (int i = 0; i < quantity; i++) {
stocks.add(createStockEntity(product));
}
return stocks;
}
private Stock createStockEntity(Product product) {
return Stock.builder()
.product(product)
.barcodeNumber(generateBarcodeNumber())
.build();
}
}
π μ±λ₯ κ°μ΄λλΌμΈ
μν© | κΆμ₯ λ°©μ | μ΄μ |
---|---|---|
μλ λ°μ΄ν° (< 1000κ°) | Stream API | κ°λ μ± μ°μ , μ±λ₯ μ°¨μ΄ λ―Έλ―Έ |
λλ λ°μ΄ν° (> 10000κ°) | Parallel Stream | λ©ν°μ½μ΄ νμ©μΌλ‘ μ±λ₯ ν₯μ |
CPU μ§μ½μ μμ | parallelStream() |
λ³λ ¬ μ²λ¦¬ ν¨κ³Ό κ·Ήλν |
π λ¬Έμ 3: Stream 체μ΄λ 볡μ‘μ±
π μλ¬ μν©
μ¬λ¬ Stream μ°μ°μ΄ 체μ΄λλμ΄ μ½λκ° λ³΅μ‘ν΄ λ³΄μ΄λ κ²½μ°μ λλ€.
// π΅ 볡μ‘ν΄ λ³΄μ΄λ Stream 체μ΄λ
List<StockResponse> result = stockRepository.findByProductCategory(category)
.stream()
.filter(stock -> stock.getProduct().getStockQuantity() > 0)
.map(stock -> StockResponse.from(stock))
.sorted(Comparator.comparing(StockResponse::productName))
.limit(20)
.collect(Collectors.toList());
π― μμΈ λΆμ
Streamμ κ° μ°μ° λ¨κ³λ₯Ό λͺ νν μ΄ν΄νμ§ λͺ»ν΄ 볡μ‘νκ² λκ»΄μ§λλ€.
π§ ν΄κ²° λ°©λ²
1λ¨κ³: μ£ΌμμΌλ‘ κ° λ¨κ³ μ€λͺ
// β
λ¨κ³λ³ μ£ΌμμΌλ‘ λͺ
ννκ²
List<StockResponse> result = stockRepository.findByProductCategory(category)
.stream() // π λ°μ΄ν°λ₯Ό μ€νΈλ¦ΌμΌλ‘ λ³ν
.filter(stock -> stock.getProduct().getStockQuantity() > 0) // π μ¬κ³ κ° μλ μνλ§ νν°λ§
.map(stock -> StockResponse.from(stock)) // π Stockμ StockResponseλ‘ λ³ν
.sorted(Comparator.comparing(StockResponse::productName)) // π μνλͺ
μΌλ‘ μ λ ¬
.limit(20) // βοΈ μμ 20κ°λ§ μ ν
.collect(Collectors.toList()); // π¦ μ΅μ’
κ²°κ³Όλ₯Ό 리μ€νΈλ‘ μμ§
2λ¨κ³: λ©μλ λΆλ¦¬λ‘ κ°λ μ± ν₯μ
@Service
@RequiredArgsConstructor
public class StockQueryService {
// β
μ£Ό λ©μλλ κ°λ¨νκ²
public List<StockResponse> getTopStocksByCategory(String category) {
return stockRepository.findByProductCategory(category)
.stream()
.filter(this::hasStock) // π― λ©μλ μ°Έμ‘°λ‘ κ°λ
μ± ν₯μ
.map(StockResponse::from) // π― μ μ λ©μλ μ°Έμ‘° νμ©
.sorted(byProductName()) // π― λ³λ λ©μλλ‘ μ λ ¬ λ‘μ§ λΆλ¦¬
.limit(20)
.collect(Collectors.toList());
}
// π§ 보쑰 λ©μλλ€λ‘ λ‘μ§ λΆλ¦¬
private boolean hasStock(Stock stock) {
return stock.getProduct().getStockQuantity() > 0;
}
private Comparator<StockResponse> byProductName() {
return Comparator.comparing(StockResponse::productName);
}
}
π¨ Stream API μ€λ¬΄ ν¨ν΄
// πͺ μνλ³ μ¬κ³ μ§κ³
Map<String, Integer> stockByCategory = stocks.stream()
.collect(Collectors.groupingBy(
stock -> stock.getProduct().getCategory(),
Collectors.summingInt(stock -> stock.getProduct().getStockQuantity())
));
// π κ°κ²©λλ³ μν λΆλ₯
Map<String, List<Product>> productsByPriceRange = products.stream()
.collect(Collectors.groupingBy(product -> {
BigDecimal price = product.getPrice();
if (price.compareTo(new BigDecimal("100000")) < 0) return "μ κ°";
if (price.compareTo(new BigDecimal("500000")) < 0) return "μ€κ°";
return "κ³ κ°";
}));
// π μ‘°κ±΄λΆ νν°λ§κ³Ό λ³ν
Optional<Product> mostExpensiveInCategory = products.stream()
.filter(product -> "μ μμ ν".equals(product.getCategory()))
.max(Comparator.comparing(Product::getPrice));
π Stream API 체ν¬λ¦¬μ€νΈ
β κΈ°λ³Έ μ¬μ©λ² νμΈ
-
IntStream.range()
λ‘ λ°λ³΅ νμ μμ± μ΄ν΄λ¨? -
.mapToObj()
λ‘ κ°μ²΄ λ³ν κ³Όμ μ΄ν΄λ¨? -
.collect(Collectors.toList())
λ‘ μ΅μ’ μμ§ μ΄ν΄λ¨?
β μ±λ₯ μ΅μ ν
-
λλ λ°μ΄ν° μ²λ¦¬ μ
parallelStream()
κ³ λ €? - λΆνμν μ€κ° μ°μ° μ κ±°λ¨?
-
ArrayList
μ΄κΈ° ν¬κΈ° μ€μ κ³ λ €λ¨?
β κ°λ μ± κ°μ
- 볡μ‘ν Stream 체μ΄λμ λ©μλλ‘ λΆλ¦¬λ¨?
- λλ€ ννμ λμ λ©μλ μ°Έμ‘° νμ©?
- κ° λ¨κ³λ³ μ£Όμ μΆκ°λ¨?
π― μ€μ νμ© μμ
μ¬κ³ κ΄λ¦¬ μμ€ν μμμ Stream API νμ©
@Service
@RequiredArgsConstructor
public class InventoryService {
private final StockRepository stockRepository;
// π μ
κ³ μ²λ¦¬ - μλλ§νΌ Stock μν°ν° μμ±
@Transactional
public void processInbound(InboundRequest request) {
Product product = findProductById(request.getProductId());
// π ν΅μ¬: Stream APIλ‘ μ¬λ¬ Stock μν°ν° μμ±
List<Stock> newStocks = IntStream.range(0, request.getQuantity())
.mapToObj(i -> Stock.builder()
.product(product)
.barcodeNumber(generateBarcodeNumber(product, i))
.build())
.collect(Collectors.toList());
stockRepository.saveAll(newStocks);
// π μν μ¬κ³ μλ μ
λ°μ΄νΈ
product.addStock(request.getQuantity());
}
// π μΉ΄ν
κ³ λ¦¬λ³ μ¬κ³ νν© μ‘°ν
public Map<String, Long> getStockCountByCategory() {
return stockRepository.findAllWithProduct()
.stream()
.collect(Collectors.groupingBy(
stock -> stock.getProduct().getCategory(),
Collectors.counting()
));
}
// β οΈ μ¬κ³ λΆμ‘± μν μλ¦Ό
public List<LowStockAlert> getLowStockAlerts(int threshold) {
return stockRepository.findAllWithProduct()
.stream()
.filter(stock -> stock.getProduct().getStockQuantity() < threshold)
.map(stock -> LowStockAlert.builder()
.productName(stock.getProduct().getName())
.currentStock(stock.getProduct().getStockQuantity())
.threshold(threshold)
.build())
.distinct()
.collect(Collectors.toList());
}
}
π λ§λ¬΄λ¦¬
μ΄μ Java Stream APIλ₯Ό νμ©ν ν¨μ¨μ μ΄κ³ κ°λ μ± λμ μ½λλ₯Ό μμ±ν μ μμ΅λλ€!
π λ€μ λ¨κ³ κΆμ₯μ¬ν
-
Optionalκ³Ό Stream μ‘°ν©:
findFirst()
,findAny()
λ± Optional λ°ν λ©μλ νμ© - Custom Collector μμ±: 볡μ‘ν μ§κ³ λ‘μ§μ μν 컀μ€ν 컬λ ν° κ΅¬ν
- μ±λ₯ μΈ‘μ : JMH(Java Microbenchmark Harness)λ₯Ό νμ©ν μ νν μ±λ₯ μΈ‘μ
π μΆκ° νμ΅ λ¦¬μμ€
- Oracle Java Stream API λ¬Έμ: https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html
- μ΄νν°λΈ μλ° 3ν: μμ΄ν 45-48 (μ€νΈλ¦Ό κ΄λ ¨)
π‘ ν΅μ¬ κΈ°μ΅ν μ
Stream APIλ β무μμ ν μ§βλ₯Ό μ μΈνλ λ°©μμΌλ‘, μ½λμ μλλ₯Ό λͺ νν νννκ³ μ μ§λ³΄μμ±μ λμ΄λ νλμ μΈ Java κ°λ°μ ν΅μ¬ κΈ°μ μ λλ€!