본문 바로가기
야미스터디/Java

[Java - Stream] 01. 스트림을 학습해야 하는 이유

by 의정부핵꿀밤 2022. 12. 7.
728x90

[Reference]

http://www.yes24.com/Product/Goods/77125987

 

모던 자바 인 액션 - YES24

자바 1.0이 나온 이후 18년을 통틀어 가장 큰 변화가 자바 8 이후 이어지고 있다. 자바 8 이후 모던 자바를 이용하면 기존의 자바 코드 모두 그대로 쓸 수 있으며, 새로운 기능과 문법, 디자인 패턴

www.yes24.com


0️⃣ Intro

대부분은 자바 애플리케이션을 작성할 때, 데이터를 처리하기 위한 방식으로 컬렉션을 만들고 이를 활용한다.

컬렉션은 데이터를 보관하기 위한 자료구조일 뿐이고, 이를 활용하기 위해서는 개발자가 반복문과 조건문을 통해 원하는 로직을 구현해야 한다.

스트림 API는 컬렉션을 사용하는 방식과는 다른 방식으로 데이터를 처리할 수 있다.

 

 

1️⃣ 컬렉션과 스트림

컬렉션과 스트림의 대표적인 차이점은 외부 반복과 내부 반복이다

 

외부 반복 (external iteration)

  • 컬렉션을 통해 데이터를 처리하는 방식을 외부 반복이라 한다.
  • 이는 개발자가 직접 작성한 반복문과 조건문을 통해 각 요소들의 데이터 처리 방식을 구현한 로직을 말한다.

 

내부 반복 (internal iteration)

  • 스트림 API는 컬렉션과 다른 방식으로 내부 반복이라 한다.
  • 반복문을 신경 쓸 필요 없이, 스트림 라이브러리 내부에서 모든 데이터가 처리되는 방식을 말한다.

 

 

2️⃣ Stream vs Collections의 비즈니스 코드 구현

데이터 처리 방식의 차이점을 확인하기 위해, 상황을 가정하고 요구사항을 Collection으로 데이터를 처리하는 코드와 Stream으로 처리하는 코드를 확인해보자.

 

ex) 혼밥 치킨집을 차리기 위해 브랜드 별로, 1인이 먹을 수 있는 가격대의 치킨은 어떤 치킨들이 있는지 조회해보도록 한다.

 

[컬렉션을 이용한 데이터 처리]

- 예시로 든 상황에 대해 컬렉션과 반복문, 조건문으로 결과물을 출력하는 코드를 작성해보도록 한다.

- singleSizeReport() 메서드의 역할을 순차적으로 나열해보면 다음과 같다

  1. 브랜드로 그룹화 된 치킨을 저장할 Map을 생성한다.
  2. 치킨 리스트를 반복문을 통해 순차적으로 조회한다.
  3. 치킨의 가격이 12,000원 이하인 치킨을 필터링한다.
  4. 가격이 필터링 된 치킨의 브랜드를 확인하고, 브랜드 별로 그룹화 된 List에 저장한다.
  5. 결과적으로 브랜드 별로 그룹화 된 데이터를 반환한다.

 

public class ChickenReport {
	private final List<Chicken> chickens;
    
    public ChickenReport(List<Chicken> chickens) {
    	this.chickens = chickens;
    }
    
    public List<Chicken> getChickens() {
    	return this.chickens;
    }
    
    public Map<ChickenBrand, List<Chicken>> singleSizeReport() {
    	// 그룹화 된 치킨을 저장할 Map을 생성
        Map<ChickenBrand, List<Chicken>> groupByBrand = new HashMap<>();
        for (Chicken chicken : this.chickens) {
        	// 12,000원 보다 적은 가격의 치킨을 필터링
            if (chicken.getPrice() <= 12_000) {
            	ChickenBrand brand = chicken.getBrand();
                List<Chicken> chickenForBrand = groupByBrand.get(brand);
                // 현재 브랜드의 그룹화된 Map에 데이터가 없으면 새로 만든다.
                if (chickenForBrand == null) {
                	chickenForBrand = new ArrayList<>();
                    groupByBrand.put(brand, chickenBrand);
                }
                // 필터링 된 치킨을 같은 브랜드의 치킨 리스트에 추가한다.
                chickenForBrand.add(chicken);
            }
        }
        return groupByBrand;
    }
}

 

 

 

[스트림을 이용한 데이터 처리]

- 일단 처음 코드를 보는 사람이 이해하는데 얼마나 고민하게 될 지 생각해본다.

- singleSizeReport() 메서드는 두 줄로 위의 내용을 설명이 가능하다.

  1. 치킨 데이터를 정의된 isSinglePrice() 라는 메서드로 필터링
  2. 위에서 필터링된 데이터를 ChickenBrand로 Grouping 된 Map으로 반환

 

public class ChickenReport {
	private final List<Chicken> chickens;
    
    public ChickenReport(List<Chicken> chickens) {
		this.chickens = chickens;
    }
    
    public List<Chicken> getChickens() {
    	return this.chickens;
    }
    
    public Map<ChickenBrand, List<Chicken>> singleSizeReport() {
		reutrn this.chickens.stream()
        	// 12,000원보다 적은 가격의 치킨을 필터링
            .filter(ChickenReport::isSinglePrice)
            // 필터링된 치킨을 같은 브랜드의 치킨 리스트에 추가한다.
           	.collect(groupingBy(Chicken::getBrand));
    }
    
    private static boolean isSinglePrice(Chicken chicken) {
    	return chicken.getPrice() <= 12_000;
    }
}

 

 

 

3️⃣ 스트림과 컬렉션의 간단한 차이

  • 외부 반복의 구현 방식은 모든 명령문에 대해 선언하여 처리하는 반면, 스트림의 경우 메서드 체이닝으로 연결만 하고도 데이터가 처리되어 가독성 면에서 보다 효율적으로 보인다.
  • 모든 상황에서 스트림이 좋다고는 할 수 없지만, 우선 다양하게 사용해보고 추후에 잘 사용하는 방법과 주의해야 하는 점에 대해서 고민해보자.
728x90

댓글