• 스트림 활용

    • 스트림 API가 지원하는 다양한 연산을 살펴봄
  • 필터링

    • 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 등을 제공

    • 쇼트서킷 평가

      • 예를 들어 여러 and 연산으로 연결된 커다란 불리언 표현식을 평가하는 경우
      • 표현식에서 하나라도 거짓이라는 결과가 나오면 나머지 연산은 필요가 없게 됨
      • 이러한 상황을 쇼트서킷이라고 함
      • 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가지 인수를 받음
        • 초깃값
        • 스트림의 두 요소를 합쳐서 하나의 값으로 만드는 데 사용할 람다
      • 따라서 두 요소에 최댓값을 반환하는 람다만 있으면 최댓값을 구할 수 있음
      • 예시 코드
  • 숫자형 스트림

  • 스트림 만들기