0%

스트림 API

스트림 은 자바API에 새로 추가된 기능으로, 데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소로 정의할 수 있다.

아래는 칼로리기준으로 요리를 정렬하는 자바7코드다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
List<Dish> lowCaloricDishes = new ArrayList<>();
for(Dish dish : menu) {
if (dish.getCalories() < 400) {
lowCaloricDishes.add(d);
}
}

Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
public int compare(Dish dish1, Dish dish2) {
return Integer.compare(dish1.getCalories(), dish2.getCalories());
}
});

Lish<String> lowCaloricDishesName = new ArrayList<>();
for(Dish dish: lowCaloricDishes) {
lowCaloricDishesName.add(dish.getName());
}

아래는 같은내용의 자바8코드다.

1
2
3
4
5
List<String> lowCaloricDishesName = menu.stream()
.filter(dish -> dish.getCalories() < 400)
.sorted(comparing(Dish::getCalories))
.map(Dish::getName)
.collect(toList());

스트림의 특징

  • 선언형 : 더 간결하고 가독성이 좋아진다.
  • 조립할 수 있음 : 유연성이 좋아진다.
  • 병렬화 : 성능이 좋아진다.

스트림 이용하기

스트림 이용 과정은 다음과 같이 세 가지로 요약할 수 있다

  • 질의를 수행할 데이터 소스
  • 스트림 파이프라링ㄴ을 구성할 중간 연산 연결
  • 스트림 파이프라인을 실행하고 결과를 만들 최종연산

스트림 활용

필터링과 슬라이싱

스트림 인터페이스는 filter 메서드를 지원한다. filter메서드는 Predicate를 인수로 받아 일치하는 모든 요소를 포함하는 스트림을 반환한다.

1
2
3
List<Dish> vegetarianMenu = menu.stream()
.filter(Dish::isVegetarian) // 채식요리인지 확인하는 메서드 레퍼런스
.collect(toList());

distinct

distinct를 사용하면 중복을 필터링한다.

1
2
3
4
5
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct() // 중복된 요소를 제거
.forEach(System.out::println);

limit

limit를 사용하면 스트림 사이즈를 제한할 수 있다.

1
2
3
4
List<Dish> dishes = menu.stream()
.filter(dish -> dish.getCalories() > 300)
.limit(3) // 요소수를 3개로 제한
.collect(toList());

skip(n)

skip(n)을 이용하면 n개 요소를 제외하고 나머지 요소를 반환한다.

1
2
3
4
List<Dish> dishes = menu.stream()
.filter(dish -> dish.getCalories() > 300)
.skip(2) // 2개 요소를 건너뛴다
.collect(toList());

매핑

매핑 은 특정 객체에서 특정 데이터를 선택하는 작업으로, SQL의 테이블에서 특정 열만 선택하는 것과 같은 작업이다.

아래 예는 메뉴에서 요리명만 추출하는 내용이다.

1
2
3
List<String dishNames = menu.stream()
.map(Dish::getName)
.collect(toList());

검색과 매칭

anyMatch

Predicate가 주어진 스트림에서 적어도 한 요소와 일치하는지 확인

1
2
3
if(menu.stream().anyMatch(Dish::isVegetarian)) {
..
}

allMatch

모든 요소가 주어진 Predicate와 일치하는지 검사

1
2
boolean isHealthy = menu.stream()
.allMatch(dish -> dish.getCalories() < 1000)

noneMatch

주어진 Predicate와 일치하는 요소가 없는지 확인

1
2
boolean isHealthy = menu.stream()
.noneMatch(dish -> dish.getCalories() >= 1000);

findAny

현재 스트림에서 임의의 요소를 반환한다.

1
2
3
Optional<Dish> dish = menu.stream()
.filter(Dish::isVegetarian) // filter와 연결해서 사용할 수 있다.
.findAny();

findFirst

스트림 내 첫번째 요소를 불러온다.

1
2
3
4
5
List<Integer> someNumbers = Arrays.asList(1,2,3,4,5);
Optional<Integer> firstSquareDicisibleByThree = someNumbers.stream()
.map(x -> x * x)
.filter(x -> x % 3 == 0)
.findFirst(); // 9

findFirst는 병렬실행에 유용하며, 반환요소가 상관없다면 findAny를 사용해도 상관없다.

리듀싱

모든 요소를 반복적으로 처리하는 과정으로 함수형 프로그래밍에서는 폴드 라고 불리기도 한다.

reduce는 두 개의 인수를 갖는다.

  • 초깃값 (생략될 수 있다)
  • BinaryOperator
1
2
3
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
// 초기값 : 0
// BinaryOperator : (a, b) -> a + b

스트림 만들기

아래의 다양한 방법으로 스트림을 만들 수 있다.

  • Stream.of : 값으로 스트림 만들기
  • Arrays.stream : 배열로 스트림 만들기
  • File.lines : 파일로 스트림 만들기
  • Stream.iterate : 함수로 무한스트림 만들기
  • Stream.generate : 함수로 무한스트림 만들기

Reference : Java 8 in Action