스트림 활용
필터링
Predicate로 필터링
filter 메서드는 Predicate를 인수로 받아 반환하는 boolean이 true인 요소들을 포함하는 스트림 반환
예시 코드
List<Dish> vegetarianMenu = menu.stream()
.filter(Dish::isVegetarain)
.collect(toList());
고유 요소 필터링
스트림은 고유 요소로 이루어진 스트림을 반환하는 distinct()
메서드 지원
예시 코드
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
// result -> 짝수 stream을 생성하고 중복을 제거한 stream을 출력
TAKEWHILE 활용
특정 조건이 맞을 때까지 반복하는 슬라이싱 메서드(정렬된 배열에서 사용)
예를 들어 정렬된 배열에서 3500원 이하인 물품의 정보를 얻는 경우 사용됨
예시 코드
List<Dish> slicedMenu1 = specialMenu
.stream()
.takeWhile(dish -> dish.getCalories() < 320)
.collect(toList());
// takeWhile에 전달된 Predicate return false인 요소부터 끊김
DROPWHILE 활용
TAKEWHILE을 이용해서 요소를 슬라이싱 했을 때 나머지에 대해 선택할 때 사용
즉, TAKEWHILE과는 반대의 연산을 수행, Predicate가 false일 때 연산을 종료하고 나머지 반환
예시 코드
List<Dish> slicedMenu2 = menu
.stream()
.dropWhile(dish -> dish.getCalories() < 320)
.collect(toList());
스트림 축소
주어진 값 이하의 크기를 갖는 새로운 스트림을 반환하는 limit(n)
메서드 지원
예시 코드
List<Dish> dishes = menu
.stream()
.filter(dish -> dish.getCalories() > 300)
.limit(3)
.collect(toList());
// limit로 지정한 3개의 결과를 채우면 즉시 결과를 반환
// 모든 요소를 필터링하지 않음
요소 건너뛰기
처음 n개 요소를 제외한 스트림을 반환하는 skip(n)
메서드를 지원
n개 이하의 스트림에 대해 skip(n)
호출하면 빈 스트림 반환
limit(n)
과 skip(n)
은 상호 보완적인 연산을 수행
예시 코드
List<Dish> dishes = menu
.stream()
.filter(d -> d.getCalories() > 300)
.skip(2)
.collect(toList());
매핑
스트림의 각 요소에 함수 적용
스트림은 함수를 인자로 받는 map()
메서드 지원, 인수로 지원된 함수는 각 요소에 적용되며 함수를 적용한 결과가 새로운 요소로 매핑
예시 코드
List<String> dishNames = menu
.stream()
.map(Dish::getName)
.collect(toList());
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(toList());
List<Integer> dishNameLengths = menu.stream()
.map(Dish::getName)
.map(String::length)
.collect(toList());
스트림의 평면화
만약 고유 문자로 이루어진 리스트를 반환하는 경우
각 문자를 split(””)
을 통해 만든 배열은 스트림 API를 사용하기 위해 2차원 스트림으로 만들어짐 → Arrays.stream()
이용
위 경우 2차원 스트림을 1차원으로 바꿔야 하면 이를 위해 flatMap()
메서드 제공
예시 코드
// List<Stream<string>> 인 2차원 형태로 반환
words.stream()
.map(word -> word.split(""))
.map(Arrays::stream)
.distinct()
.collect(toList());
// flatMap을 통해 1차원 반환
words.stream()
.map(word -> word.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(toList());
검색과 매칭
특정 속성이 데이터 집합에 있는지 여부를 검색하는 데이터 처리도 자주 사용됨
allMatch, anyMatch, noneMatch, findFirst, findAny
등을 제공
쇼트서킷 평가
allMatch, noneMatch, findFirst, findAny
등의 연산은 모든 스트림의 요소를 처리하지 않고도 결과 반환프레디케이트가 적어도 한 요소와 일치하는지 확인
주어진 스트림에서 적어도 한 요소와 일치하는지 확인할 때 anyMatch()
메서드 활용
anyMatch()
boolean을 반환하므로 최종 연상에 해당
예시 코드
if(menu.stream().anyMatch(Dish::isVegetarian) {
System.out.println("The menu is (somewhat) vegeterian friendly!!");
}
프레디케이트가 모든 요소와 일치하는지 검사
스트림의 모든 요소가 주어진 프레디케이트와 일치하는지 확인할 때 allMatch()
메서드 활용
예시 코드
// 모든 메뉴 칼롤리가 1000미만인 경우 true 반환
boolean isHealthy = menu.stream()
.allMatch(dish -> dish.getCalories() < 1000);
프레디케이트가 모든 요소와 일치하지 않는지 검사
allMatch()
와 정반대의 연산을 수행하는 noneMatch()
메서드 제공
예시 코드
// 모든 메뉴가 칼로리 1000이상이지 않을 경우 true 반환
boolean isHealthy = menu.stream()
.noneMatch(d -> d.getCalories() >= 1000);
요소 검색
현재 스트림에서 임의의 요소를 반환하는 findAny()
메서드 제공
예시 코드
// findAny는 아무 요소도 반환하지 않을 수도 있으므로 Optional 타입으로 반환
// isPresent() -> 값이 있다면 true, 없다면 false
// ifPresent(Consumer<T> block) -> 값이 있다면 주어진 블록 실행
// T get() -> 값이 존재하면 값을 반환, 값이 없으면 NoSuchElementException
// T orElse(T other) -> 값이 있으면 값 반환 없다면 기본값 반환
Optional<Dish> dish = menu.stream()
.filter(Dish::isVegeterian)
.findAny();
첫 번째 요소 검색
스트림에서 첫 번째 요소를 검색하는 findFirst()
메서드 제공
예시 코드
List<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstSquareDivisibleByThree = someNumbers.stream()
.map(n -> n * n)
.filter(n -> n % 3 == 0)
.findFirst();
// result -> 9
findAny, findFirst
findAny
사용리듀싱
요소의 합
초깃값을 받지 않는 reduce가능하나 Optional 객체를 반환
스트림의 요소가 없는 경우를 대비에 Optional로 반환
예시 코드
// 초깃값 없는 경우
Optional<Integer> sum = numbers.stream().reduce((a, b) -> (a + b);
// 초깃값 있는 경우
int sum = numbers.stream().reduce(0, (a, b) -> (a + b));
최댓값과 최솟값
reduce는 기본적으로 2가지 인수를 받음
따라서 두 요소에 최댓값을 반환하는 람다만 있으면 최댓값을 구할 수 있음
예시 코드
Optional<Integer> max = numbers.stream().reduce(Integer::max);
Optional<Integer> min = numbers.stream().reduce(Integer::min);
숫자형 스트림
기본적으로 Stream은 박싱이 이루어지기 때문에 숫자 Stream의 경우 Integer로 오토박싱이이 이루어짐
따라서 sum()
메서드를 호출해서 간단하게 사용이 어려움
이를 위해 기본형 특화 스트림 제공
기본형 특화 스트림
박싱 비용을 피할 수 있도록 int 요소에 특화된 IntStream, double요소에 특화된 DoubleStream 제공
각 int, double 인터페이스는 sum, max
등의 메서드를 제공, 원할 때 다시 객체 스트림으로 변환하는 기능도 제공
예시 코드
// 숫자 스트림으로의 변환
// 스트림이 비어있는 경우 0 반환
int calories = menu.stream()
.mapToInt(Dish::getCalories)
.sum();
// 객체 스트림으로 변환
IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
Stream<Integer> stream = intStream.boxed();
기본값: OptionalInt
앞서 합계에제에서는 스트림이 빈 경우 0을 반환하는데, 이는 최댓값/최솟값을 가져오는 경우 문제가 될 수 있음
최대값이 0인 경우 기본값인지 확인이 불가능
이를 위해 OptionalInt, OptionalDouble, OptionalLong 제공
예시 코드
OptionalInt maxCalories = menu.stream()
.mapToInt(Dish::getCalories)
.max();
int max = maxCalories.orElse(1) // 이를 통해 값이 없는 경우 기본값도 적용 가능
숫자 범위
특정 범위의 숫자를 이용하는 경우 사용가능하며 range(), rangeClosed()
메서드 제공
각 메서드 차이는 종료값을 포함하거나 포함하지 않는 차이
예시 코드
IntStream evenNumbers = IntStream.rangeClosed(1, 100)
.filter(n -> n % 2 == 0);
System.out.println(evenNumbers.count());
스트림 만들기
값으로 스트림 만들기
임의의 수를 인수로 받는 정적 메서드 Stream.of()
를 이용해 스트림을 만들 수 있음
예시 코드
// String 스트림 생성
Stream<String> stream = Stream.of("Modern", "Java", "In", "Action");
stream.forEach(System.out::println);
// 스트림을 비울 수도 있음
Stream<String> emptyStream = Stream.empty();
null이 될 수 있는 객체로 스트림 만들기
ofNullable()
메서드 추가배열로 스트림 만들기
파일로 스트림 만들기
함수로 무한 스트림 만들기