필터링
프레디케이트로 필터링
스트림 인터페이스는 filter 메서드를 지원한다. filter 메서드는 프레디케이트(boolean을 반환)를 인수로 받아서 프레디케이트와 일치하는 모든 요소를 포함하는 스트림을 반환한다.
List<Dish> menu = Arrays.asList(
new Dish("pork", false, 800, Type.MEAT),
new Dish("beaf", false, 700, Type.MEAT),
new Dish("chichen", false, 400, Type.MEAT),
new Dish("french fries", true, 530, Type.OTHER),
new Dish("rice", true, 350, Type.OTHER),
new Dish("season fruit", true, 120, Type.OTHER),
new Dish("pizza", true, 550, Type.OTHER),
new Dish("prawns", false, 300, Type.FISH),
new Dish("salmon", false, 450, Type.FISH)
);
List<Dish> vegetarianMenu = menu.stream()
.filter(Dish::isVegetarian) // 채식 요리인지 확인하는 메서드
.collect(Collectors.toList());
위에 코드처럼 모든 채식요리를 필터링해서 채식 메뉴를 만들 수 있다.
고유 요소 필터링
스트림은 고유 요소로 이루어진 스트림을 반환하는 distinct 메서드도 지원한다.(고유 여부는 스트림에서 만든 객체의 hashcode, equals로 결정함) 아래와 같은 코드는 리스트의 모든 짝수를 선택하고 중복을 제거한다.
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
스트림 슬라이싱
프리디케이트를 이용한 슬라이싱
TAKEWHILE 활용
List<Dish> specialMenu = Arrays.asList(
new Dish("season fruit", true, 120, Type.OTHER),
new Dish("prawns", false, 300, Type.FISH),
new Dish("rice", true, 350, Type.OTHER),
new Dish("chicken", false, 400, Type.MEAT),
new Dish("french fries", true, 530, Type.OTHER));
다음과 같은 specialMenu 에서 320 칼로리 이하의 요리를 선택하려면
List<Dish> filteredMenu = specialMenu.stream()
.filter(dish -> dish.getCalories() <320)
.collect(Collectors.toList());
위에 코드처럼 작성하면 된다. 하지만 위 리스트는 이미 칼로리 순으로 정렬되어 있다. filter 연산을 이용하면 전체 스트림을 반복하면서 각 요소에 프레디케이트를 적용하게 된다. 따라서 리스트가 이미 정렬되어 있다는 사실을 이용해 320칼로리보다 크거나 같은 요리가 나왔을 때 반복 작업을 중단 할 수 있다. 이때 아래 처럼 takeWhile 연산을 이용하면 된다.
List<Dish> filteredMenu = specialMenu.stream()
.takeWhile(dish -> dish.getCalories() <320)
.collect(Collectors.toList());
DROPWHILE 활용
나머지 요소를 선택하려면 어떻게 해야할까? 즉 320칼로리 보다 큰 요소는 어떻게 참색할까
이때 dropWhile을 이용해 작업 할 수 있다.
List<Dish> filteredMenu = specialMenu.stream()
.dropWhile(dish -> dish.getCalories() <320)
.collect(Collectors.toList());
dropWhile는 takeWhile랑 정 반대의 작업을 수행한다. dropWhile은 프레디케이트가 처음으로 거짓이 되는 지점까지 발견된 요소를 버린다. 프레디케이트가 거짓이 되면 그 지점에서 작업을 중단하고 남은 모든 요소를 반환 하는것이다.
스트림 축소
스트림은 주어진 값 이하의 크기를 갖는 새로운 스트림을 반환하는 limit 메서드를 지원한다. 스트림이 정렬되어 있으면 최대 요소 n개를 반환할 수 있다. 예를 들어 아래 코드처럼 300칼로리 이상의 세 요리를 선택해서 리스트를 만들 수 있다.
List<Dish> dishes = specialMenu.stream()
.filter(dish -> dish.getCalories() > 300)
.limit(3)
.collect(Collectors.toList());
요소 건너뛰기
스트림은 처음 n개 요소를 제외한 스트림을 반환하는 skip 메서드를 지원한다. n개 이하의 요소를 포함하는 스트림에 skip을 호출하면 빈 스트림이 반환된다. limit과 skip은 상호 보완적인 연산을 수행한다. 예를 들어 다음 코드는 300칼로리 이상의 처음 두 요리를 건너뛴 다음에 300칼로리가 넘는 나머지 요리를 반환한다.
List<Dish> dishes = specialMenu.stream()
.filter(dish -> dish.getCalories() > 300)
.skip(2)
.collect(Collectors.toList());
매핑
스트림 API의 map과 faltMap메서드는 특정 데이터를 선택하는 기능을 제공한다. 위에 메뉴에서 이름들을 추출하는 코드를 보자
List<String> dishNames = menu.stream()
.map(Dish::getName)
.collect(Collectors.toList());
getNames의 메서드는 문자열을 반환하므로 map메서드의 출력 스트림은 Stream<String> 형식을 갖는다. 만약 요리명의 길이를 알고 싶다면 어떻게 해야할까?
List<Integer> dishNames = menu.stream()
.map(Dish::getName)
.map( d->d.length()) // (String::length)
.collect(Collectors.toList());
다른 map 메서드를 연결 할 수 있다.
스트림 평면화
리스트에서 고유 문자로 이루어진 리스트를 반환해보자. 예를 들어 ["Hello", "World"] 리스트가 있다면 결가로 ["H" , "e" , "l" , "o" , "w" , "r" , "d"] 를 포함하는 리스트가 반환 되어야한다. 이때 flatMap 메서드로 스트림의 각 값을 다른 스트림으로 만든 다음에 모든 스트림을 하나의 스트림으로 연결하는 기능을 사용한다.
'Study > 모던 자바 인 액션' 카테고리의 다른 글
3장 람다 표현식 (0) | 2022.08.17 |
---|---|
2장 - 동작 파라미터화 코드 전달하기 (0) | 2022.07.06 |