[Java8] 스트림 활용

스트림 활용

목차

프레디케이트로 필터링

List<Dish> vegetarianMenu =
                menu.stream()
                      .filter(Dish::isVegetarian)
                      .collect(toList());

프레디케이트로 필터링

고유 요소 필터링

List<Integer> numbers = Arrays.asList(1, 2 ,1, 3, 3, 2, 4);
numbers.stream().filter (i -> i % 2 == 0)
                .distinct()
                .forEach(System.out::println);

고유 요소 필터링

스트림 축소

List<Dish> dishes = menu.stream()
                            .filter(d -> d.getCalories() > 300)
                            .limit(3)
                            .collect(toList());

스트림 축소

요소 건너뛰기

List<Dish> dishes = menu.stream()
                            .filter(d -> d.getCalories() > 300)
                            .skip(2)
                            .collect(toList());

매핑

스트림의 각 요소에 함수 적용하기

// 요리명 추출하는 코드
List<Dish> dishes = menu.stream()
                            .map(Dish::getName)
                            .collect(toList());

// 요리명의 길이도 구하고  싶다면
List<Dish> wordLengths = menu.stream()
                                .map(Dish::getName)
                                .map(String::length)
                                .collect(toList());

스트림 평면화

List<String> uniqueCharacters =
                  words.stream()
                          .map(w -> w.split(""))
                          .flatMap(Arrays::stream)
                          .distinct()
                          .collect(toList());

flatMap

검색과 매칭

프레디케이트가 적어도 한 요소와 일치하는지 확인

if(menu.stream().anyMatch(Dish::isVegetarian)) {
  System.out.println("The menu is (somewhat) vegetarian friendly!!");
}

프레디케이트가 모든 요소와 일치하는지 검사

boolean isHealthy = menu.stream()
                            .allMatch(d -> d.getCalories() < 1000 );

noneMatch

boolean isHealthy = menu.stream()
                            .noneMatch(d -> d.getCalories() >= 1000);

요소 검색

Optional<Dish> dish =
                 menu.stream()
                        .filter(Dish::isVegetarian)
                        .findAny();

첫 번째 요소 찾기

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

findFirstfindAny는 일반적으로 결과과 같으나 병렬성 때문에 둘이 존재한다. 병렬 스트림에서는 제약이 없는 findAny를 사용한다.

리듀싱

요소의 합

// sum 변수의 초기값 0
// 리스트의 모든 요소를 조합하는 연산
int sum = 0;
for (int x : numbers) {
  sum += x;
}

// 위 코드를 reduce를 이용하면...
int sum = numbers.stream().reduce(0, (a,b) -> a + b);

// 모든 요소 곱셈 적용시
int sum = numbers.stream().reduce(0, (a,b) -> a * b);

요소의 합

초기값 없음
Optional<Integer> sum = numbers.stream().reduce((a,b) -> a + b);

최댓값과 최솟값

// 최대값
Optional<Integer> max = numbers.stream().reduce(Integer::max);

// 최소값
Optional<Integer> min = numbers.stream().reduce(Integer::min);

최댓값과 최솟값

전체 연산 요약

전체 연산 요약

숫자형 스트림

기본 특화 스트림

숫자 스트림으로 매핑
// reduce 메서드로 스트림 요소의 합 계산
// 칼로리 합계 계산
int calories = menu.stream()
                      .map(Dish::getCalories)
                      .reduce(0, Integer::sum);


/*
// 틀린 코드
Stream<Dish>는 sum이라는 연산 수행 불가
int calories = menu.stream()
                      .map(Dish::getCalories)
                      .sum();
*/                      

// mapTolnt, mapToDouble, mapToLong 사용시 가능
int calories = menu.stream()
                      .mapTolnt(Dish::getCalories)
                      .sum();
객체 스트림으로 복원
IntStream intStream = menu.stream().mapTolnt(Dish::getCalories);
Stream<Integer> stream = intStream.boxed();
기본값: OptionalInt
Optionallnt maxCalories = menu.stream()
                    .mapTolnt(Dish::getCalories)
                    .max();

// 값이 없을 때 기본 최댓값을 명시적으로 설정
int max = maxCalories.orElse(1);

숫자 범위

// 1부터 100까지의 짝수 스트림
IntStream evenNumbers = IntStream.rangeClosed(1,100).filter (n-> n % 2 == 0);

스트림 만들기

값으로 스트림 만들기

// 모든 문자열을 대문자로 변환 후 문자열 하나씩 출력
Stream<String> stream = Stream.of( "Java 8", "Lambdas", "In", "Action");
stream.map(String::toUpperCase).forEach(System.out::println);

// 스트림을 비울 수 있다.
Stream<String> emptyStream = Stream.empty();

배열로 스트림 만들기

int [] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();

파일로 스트림 만들기

long uniqueWords = 0;
try (Stream<String) lines = Files.lines(Paths .get("data.txt"), Charset.defaultCharset()))
{
  uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
                            .distinct()
                            .count();
}
catch (IOException e)
{
}

함수로 무한 스트림 만들기

// iterate
Stream.iterate(0, n -> n + 2)
                  .limit(10)
                  .forEach(System.out::println);

// generate
Stream.generate(Math::Random)
                  .limit(5)
                  .forEach(System.out::println);

요약