ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스트림 활용하기, 중간연산과 최종연산 #filter #map #findAny #reduce
    JAVA 2023. 3. 26. 15:53

     

    스트림 활용하기, 중간연산과 최종연산 

    #filter #distinct

    #takeWhile #dropWhile #skip #limit

    #map #flatMap

    #anyMatch #allMatch #noneMatch

    #findAny #findFirst


     

     

    안녕하세요? 장장스입니다.

    오늘은 스트림 연산에 대해 정리해 보겠습니다.

     

    1. 스트림 요소 필터링


    filter 메서드

    Stream<T> filter(Predicate<? super T> predicate);

    fillter 메서드는 프레디케이트(Predicate)를 인수로 받아서 프레디케이트와 일치하는 모든 요소를 포함하는 스트림을 반환한다.

    public static final List<Dish> menu = Arrays.asList(
          new Dish("pork", false, 800, Dish.Type.MEAT),
          new Dish("beef", false, 700, Dish.Type.MEAT),
          new Dish("chicken", false, 400, Dish.Type.MEAT),
          new Dish("french fries", true, 530, Dish.Type.OTHER),
          new Dish("rice", true, 350, Dish.Type.OTHER),
          new Dish("season fruit", true, 120, Dish.Type.OTHER),
          new Dish("pizza", true, 550, Dish.Type.OTHER),
          new Dish("prawns", false, 400, Dish.Type.FISH),
          new Dish("salmon", false, 450, Dish.Type.FISH)
      );

    위와 같이 menu 리스트가 있을 때,

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

    isVegetarian 메서드는 Dish 인스턴스가 채소인지 확인하는 void->boolean 형 메서드이다.

    출력 결과를 보면 다음과 같다.

    french fries
    rice
    season fruit
    pizza

     

    distinct 메서드

    Stream<T> distinct();

    distinct 메서드는 고유 요소를 필터링 하는 메서드이다.

    고유 여부는 스트림에서 만든 객체의 hashCode, equals로 결정이 된다.

    List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);

    위와 같이 numbers 리스트가 있을때, 

    numbers.stream()
           .filter(i -> i % 2 == 0)
           .distinct()
           .forEach(System.out::println);

    짝수를 필터링하여 distinct 메서드를 실행하면, 다음과 같이 중복된 숫자가 제거 되어 출력된다.

    2
    4

     

    2. 스트림 슬라이싱


    자바 9부터는 스트림의 요소를 선택할 수 있는 takeWhile, dropWhile 메서드를 지원한다.

    List<Dish> specialMenu = Arrays.asList(
            new Dish("season fruit", true, 120, Dish.Type.OTHER),
            new Dish("prawns", false, 300, Dish.Type.FISH),
            new Dish("prawns and chips", false, 320, Dish.Type.FISH),
            new Dish("rice", true, 350, Dish.Type.OTHER),
            new Dish("chicken", false, 400, Dish.Type.MEAT),
            new Dish("french fries", true, 530, Dish.Type.OTHER)
            );

    위와 같은 specialMenu 리스트가 있다.

     

    takeWhile 메서드

    takeWhile 메서드는 조건에 맞는 요소가 나왔을 때부터 반복 작업을 수행한다.

    List<Dish> slicedMenu1 = specialMenu.stream()
                    .takeWhile(dish -> dish.getCalories() < 320)
                    .collect(toList());
                    
    slicedMenu1.forEach(System.out::println);

    다음은 출력 결과이다.

    season fruit
    prawns

     

    dropWhile 메서드

    dropWhile 메서드는 takeWhile 의 정반대로 조건에 맞는 요소가 나올 때부터 반복작업을 수행한다.

    List<Dish> slicedMenu2 = specialMenu.stream()
            .dropWhile(dish -> dish.getCalories() < 320)
            .collect(toList());
            
    slicedMenu2.forEach(System.out::println);

    다음은 출력 결과이다.

    prawns and chips
    rice
    chicken
    french fries

     

    limit 메서드

    주어진 값 이하의 크기를 갖는 새로운 스트림을 반환한다.

    List<Dish> dishesLimit3 = menu.stream()
                    .filter(d -> d.getCalories() > 300)
                    .limit(3)
                    .collect(toList());
    
    dishesLimit3.forEach(System.out::println);

    limit의 요소 3만큼 선택하여 리스트가 만들어진 것을 확인 할 수 있다.

    pork
    beef
    chicken

     

    skip 메서드

    limit 메서드와 정반대로 처음 선택한 요소의 개수만큼을 제외한 스트림을 반환 할 수 있다.

     List<Dish> dishesSkip2 = menu.stream()
                    .filter(d -> d.getCalories() > 300)
                    .skip(2)
                    .collect(toList());
    
    
    dishesSkip2.forEach(System.out::println);

    처음 요소 2개를 제외한 모든 요소가 출력이 되는 것을 확인 할 수 있다.

    chicken
    french fries
    rice
    pizza
    prawns
    salmon

     

     

    3. 스트림 요소 매핑


    map 메서드

    스트림은 함수를 인수로 받는 map 메서드를 지원한다.

    <R> Stream<R> map(Function<? super T, ? extends R> mapper);

    인수로 제공된 함수는 각 요소에 적용되며 함수를 적용한 결과가 새로운 요소로 매핑된다.

    Function은 T->R 함수형 인터페이스!

     

    List<String> words = Arrays.asList("Hello", "World", "Stream API", "map");
    List<Integer> wordLengths = words.stream()
            .map(String::length)
            .collect(toList());
    
    System.out.println(wordLengths);

    map 메서드에 인자로 들어간 length 함수의 반환값이 int로 stream 요소의 값이 String에서 Integer로 매핑되는 것을 알 수 있다.

    [5, 5, 10, 3]

     

    flatMap

    flatMap 메서드는 배열을 스트림이 아니라 스트림의 콘텐츠로 매핑한다. 하나의 평면화된 스트림을 반환한다.

    List<String> words = Arrays.asList("Hello", "World", "Stream API", "map");

    위 문자열 배열에서 중복을 제외한 알파벳을 뽑아보자.

    words.stream()
        .map(word -> word.split(""))
        .flatMap(Arrays::stream)
        .distinct()
        .collect(toList());

    flatMap 메서드는 문자열 배열(String[])을 하나의 평면화된 스트림으로 반환한다. 반환의 과정은 사진과 같다.

     

    4.스트림 요소 매칭


     

    anyMatch 메서드

    프레디케이트로 적어도 한 요소와 일치하는지 확인하는 메서드이다.

    menu.stream()
        .anyMatch(Dish::isVegetarian);

     

     

    allMatch 메서드

    스트림의 모든 요소가 주어진 프레디케이트와 일치하는지를 검사한다.

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

     

    noneMatch 메서드

    allMatch 메서드와 정반대 연산을 수행한다. 스트림의 모든 요소가 일치하는 요소가 없는지 확인한다.

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

     

    5. 스트림 요소 검색


     

    findAny 메서드

    현재 스트림에서 임의의 요소를 반환한다. findAny 메서드를 다른 스트림연산과 연결해서 사용할 수 있다.

    menu.stream()
        .filter(Dish::isVegetarian)
        .findAny();

     

    findFirst 메서드

    리스트 또는 정렬된 연속된 데이터로부터 생성된 스트트림에서 논리적인 아이템 순서가 정해져 있을 수 있다. 이런 스트림에서 첫 번째 요소를 찾아 내는 메서드이다.

    menu.stream()
        .filter(Dish::isVegetarian)
        .findFirst();

     

     

    6. 리듀싱 (reduce)


    스트림에서는 스트림의 모든 요소를 처리해서 값으로 도출하는 리듀싱 연산을 지원한다.

     

    reduce 메서드

    reduce 연산은 초깃값두요소를 조합해서 새로운 값을 만드는 BinaryOperator<T>를 2개의 인수로 갖는다.

    List<Integer> numbers = Arrays.asList(3, 4, 5, 1, 2);
    int sum = numbers.stream().reduce(0, (a, b) -> a + b);
    System.out.println(sum);

    위 예제에서는 BinaryOperator 대신 람다 표현식 (a, b) -> a+b 를 사용했다.

    아래는 출력 결과이다.

    15

    reduce 연산은 스트림이 하나의 값이 될때까지 각 요소를 반복해서 조합한다.

     

     

    초깃값이 없는 reduce 메서드

    Optional<Integer> min = numbers.stream().reduce((a,b) -> a+b);

    이처럼 초깃값을 설정하지 않는 reduce도 있다. 여기서 주의할 점은 Optional을 반환하는 것이다. 예를 들어, 스트림에 아무 요소가 없는 경우 값을 반환 할 수 없다. 때문에 Optional을 감싸 값을 반환한다.

     

    reduce를 사용한 최댓값, 최솟값

    reduce연산을 활용하여 최댓값과 최솟값을 연산 할 수 있다.

    Optional<Integer> min = numbers.stream().reduce(Integer::min);
    Optional<Integer> min = numbers.stream().reduce(Integer::max);

     

     

     

     

    Post

     


    References


    • 모던 자바 인 액션

     

     


    잘못된 코드나 내용이 있다면 댓글을 남겨주세요. 즉시 수정하도록 하겠습니다! :)

     

     

    댓글