JAVA/Modern JAVA

[ModernJAVA] Stream이 필요한 이유

IT록흐 2024. 3. 5. 14:05
반응형

 

컬렉션(Collection)은 데이터를 그룹화시켜 처리할 수 있도록 도와주는 자료구조이다. JAVA는 자료구조에 저장된 데이터를 조작할 수 있도록 Collection 인터페이스를 제공한다.

 

그러나 문제가 있다. 

 

멀티코어 환경이 대중화 되면서 병렬 프로세스가 중요해졌다. 컬렉션에 저장된 데이터를 여러 스레드에 분산처리하면 높은 성능을 낼 수 있기 때문이다. 그러나 그 과정을 코드로 구현하기란 여간 복잡한 것이 아니다. JAVA는 명령형 프로그래밍이기에, 과정을 하나하나 코드로 구현해야만 한다.

 

 

 

 

음식 컬렉션을 세 가지 과정을 거쳐 조작해보자. 

 

1) 400 칼로리 이하인 음식으로 필터링 한다. 

2) 칼로리가 낮은 순으로 정렬한다. 

3) 음식의 이름만 추출한다. 

 

세 가지 과정을 명령형 프로그래밍으로 개발하면 아래와 같다. 

 

명령형 프로그래밍

// 1) 400 칼로리 이하인 음식들로만 필터링 하기 
List<Dish> lowCaloricDishes = new ArrayList<>(); // 가비지 변수 ( 컨테이너 )

for(Dish dish : dishes){
  if(dish.getCalories() < 400){
      lowCaloricDishes.add(dish);
  }
}

// 2) 칼로리가 낮은 순으로 정렬하기 
Collections.sort(lowCaloricDishes, new Comparator<Dish>(){
	public int compare(Dish dish1, Dish dish2){
    	return Integer.compare(dish1.getCalories(),dish2.getCalories());
    }
}); 

// 3) 음식 이름만 추출해서 리스트로 변환하기 
List<String> lowCaloricDishNames = new ArrayList<>();
for(Dish dish : lowCaloricDishes){
  lowCaloricDishNames.add(dish.getName());
}

 

 

명령형 프로그래밍은 동작방식을 개발자가 코드로 구현해야 하기에, 코드가 복잡해질 수 밖에 없다.  여기에 병렬로 컬렉션의 데이터를 처리하려면 더욱 복잡해진다. 

 

그러므로 복잡한 로직을 내부로 숨기고 명령이 아닌 '선언'을 하는 방식이 필요하다. 이것이 선언형 프로그래밍이다. '선언'이란 과정이 아닌 결과를 의미한다. 명령형 프로그래밍은 과정을 코드로 작성하지만 선언형 프로그래밍은 결과를 선언만 하면 된다. 그러면 내부로직이 알아서 동작하여 처리한다.

 

이는 마치 함수와 같다. 함수를 호출하는 클라이언트는 함수 내부동작을 모른다. 그저 함수가 어떤 기능을 하는지만 알고 있다. JAVA는 JAVA8부터 함수형 프로그래밍을 지원한다. JAVA8부터는 컬렉션을 선언형 프로그래밍 방식으로 조작이 가능해진 것이다.  

 

 

선언형 프로그래밍

dishes.stream()
      .filter(d->d.getCalories() < 400 ) // 주어진 조건으로 필터링 하라는 선언 
      .sorted(comparing(Dish::getCalories)) // 주어진 조건으로 정렬 하라는 선언
      .map(Dish::getName) // 주어진 조건으로 매핑하라는 선언
      .collect(toList()); // 주어진 형식으로 변환하라는 선언

 

 

필터링, 정렬, 매핑, 변환 과정을 모두 코드로 구현해야 했던 명령형 프로그래밍과는 달리, Stream 인터페이스를 활용하면 그저 '선언'만 해주면 된다. 필요한 조건은 람다표현식으로 넘기면 된다. 명령형 프로그래밍과 비교했을 때, 놀랍도록 코드가 간결해졌다. 복잡한 로직은 모두 Stream 내부에 숨겨져 있기 때문이다. 

 

컬렉션 처리를 병렬로 하고 싶다면 간단한 선언만 추가하면 된다.

 

dishes.parallelStream() // 병렬처리 선언
      .filter(d->d.getCalories() < 400 ) // 주어진 조건으로 필터링 하라는 선언 
      .sorted(comparing(Dish::getCalories)) // 주어진 조건으로 정렬 하라는 선언
      .map(Dish::getName) // 주어진 조건으로 매핑하라는 선언
      .collect(toList()); // 주어진 형식으로 변환하라는 선언

 

 

이렇게 선언하면 Stream 내부에서 알아서 병렬구조로 처리하여 좋은 성능을 낼 수 있다. 

 

이와같이,

JAVA는 JAVA8부터 컬렉션을 간단한 선언형 프로그래밍으로 처리할 수 있도록 Stream 인터페이스를 제공하고 있다. 선언형 프로그래밍이 가해져서 간결한 코드 구현이 가능해졌고 병렬처리도 단순해졌다. 또한 빌더패턴 구조로 이루어져 있어 원하는 처리를 파이프라이닝으로 선언만 하면되므로, 유연하기까지 하다. 

 


 

 

참고자료

 

 

모던 자바 인 액션 : 네이버 도서

네이버 도서 상세정보를 제공합니다.

search.shopping.naver.com

 

반응형