Backend

모던 자바 인 액션 - 스트림 활용

연_우리 2022. 7. 16. 23:19
반응형

목차

     

     

     

    필터링

    Predicate 필터링

    filter는 프레디케이트(boolean을 반환하는 함수)를 인수로 받아서 true로 반환되는 요소를 모은 스트림을 반환한다

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

     

    Distict 필터링

    distinct는 객체를 hashCode, equals로 비교하여 고유한 요소를 모은 스트림을 반환한다

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

     

     

     

    슬라이싱

    칼로리 순으로 정렬된 메뉴 리스트에서 320 칼로리 이하의 요소를 선택하려면 어떻게해야할까??

     

    filter를 사용할 수 있다. filter는 스트림의 모든 요소에 프레디케이트를 적용하는데,

    리스트가 이미 정렬된 상태에서 320 칼로리 초과가 나오면 그 이후로 프레디케이트가 적용되는 의미가 있을까?? 없다.

     

     

     

    takeWhile은 정렬된 스트림에서 false가 나오면 반복을 멈추고 요소의 시작~멈춘 시점까지 반환한다.

    takeWhile은 정렬된 스트림에서 320칼로리 이하를 모으고, 320칼로리 초과인 메뉴가 나왔을 때 반복작업을 중단한다.

    List<Dish> slicedMenu1 = menu.stream()
        .takeWhile(dish -> dish.getCalories() < 320)
        .collect(toList());

     

    dropWhile은 정렬된 스트림에서 false가 나오면 반복을 멈추고 멈춘시점~요소의 끝까지 반환한다.

    dropWhile은 정렬된 스트림에서 320칼로리 이하를 버리고, 320칼로리 초과인 메뉴가 나왔을 때 반복작업을 중단한다.

    List<Dish> slicedMenu1 = menu.stream()
        .dropWhile(dish -> dish.getCalories() < 320)
        .collect(toList());

     

     

     

    Limit 축소

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

    limit(n)이면 최대 n개를 반환할 수 있다.

    List<Dish> dishes = menu.stream()
        .filter(dish -> dish.getCalories() > 300)
        .limit(3)
        .collect(toList());
    ----------------------------------------
    최대 3개가 반환된다.

     

     

     

    skip 건너뛰기

    처음 n개를 제외한 스트림을 반환한다.

    List<Dish> dishes = menu.stream()
        .filter(dish -> dish.getCalories() > 300)
        .skip(2)
        .collect(toList());
    -------------------------------------
    처음에서 2개를 건너뛴 스트림을 반환한다

     

     

     

    Mapping 맵핑

    각 요소에 함수를 적용하는 map

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

    함수는 각 요소에 적용되어 새로운 요소로 맵핑된다.

    (Dish객체의 Stream에 Dish::getName함수를 적용하여 String의 List를 반환한다)

     

    map은 map끼리 연결할 수 있다.

    List<String> dishNames = menu.stream()   //Dish객체의 Stream
        .map(Dish::getName)                  //각 메뉴의 이름을 반환한다
        .collect(toList());
    ---------------------------------------
    List<Integer> dishNames = menu.stream()  //Dish객체의 Stream
        .map(Dish::getName)                  //각 메뉴의 이름을 반환한다
        .map(String::length)                 //메뉴 이름의 길이를 반환한다
        .collect(toList());

     

     

    스트림 평면화 flatMap

    ["Hello", "World"] 리스트에서 고유 문자로 이루어진 ["H", "e", "l", "o", "W", "r", "d"] 를 반환하려면 어떻게 해야할까?

     

    map을 이용해서 문자를 나누고, distinct로 중복을 제거하면될까?

    결과적으로, 이 방법은 실패하게된다.

    words.stream()
    	.map(s -> s.split(""))     //단어를 개별 문자의 배열로 반환
    	.distinct()
    	.collect(toList());
    ---------------------------------------
    map 
    	s = "Hello" 일때 : ["H", "e", "l", "l", "o"]
    	s = "World" 일때 : ["W", "o", "r", "l", "d"]
    
    distinct
    	["H", "e", "l", "l", "o"] 묶음과 ["W", "o", "r", "l", "d"] 묶음 자체 비교 -> 중복된 묶음 아님

     

     

    flatMap은 각 배열의 값을 단일 원소 스트림으로 반환할 수 있다

    words.stream()
        .map(s -> s.split(""))      //단어를 개별 문자의 배열로 반환
        .flatMap(Arrays::stream)    //배열의 요소를 개별 스트림으로 반환
        .distinct()
        .collect(toList());
    ---------------------------------------
    map 
    	s = "Hello" 일때 : ["H", "e", "l", "l", "o"]
    	s = "World" 일때 : ["W", "o", "r", "l", "d"]
        
    flatMap
    	Arrays::stream은 배열을 스트림으로 반환해준다.
    	반환값 : ["H", "e", "l", "l", "o", "W", "o", "r", "l", "d"]
    
    distinct
    	["H", "e", "l", "l", "o", "W", "o", "r", "l", "d"] 묶음에서 중복제거

     

     

     

    반응형

     

     

    일치 검사 any / all / noneMatch

    적어도 한 요소와 일치하는지 확인 anyMatch

    하나라도 채식요리이면 true가 반환된다.

    boolean vegetarian = menu.stream().anyMatch(Dish::isVegetarian);

     

    모든 요소와 일치하는지 확인 allMatch

    모든 요리가 채식요리여야 true가 반환된다.

    boolean vegetarian = menu.stream().allMatch(Dish::isVegetarian);

     

    모든 요소와 일치하지 않는지 확인 noneMatch

    모든 요리가 채식요리이지 않아야 true가 반환된다.

    boolean vegetarian = menu.stream().noneMatch(Dish::isVegetarian);

     

     

     

     

    요소 검색 findAny / findFirst

    일치하는 임의의 요소 반환 findAny

    병렬스트림에서는 첫번째 요소를 찾기 어렵다.

    요소의 반환순서가 상관없다면 findAny를 사용하자.

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

     

     

    일치하는 첫번째 요소 반환 findFirst

    병렬 스트림에서는 첫번째 요소를 찾기 어렵다.

    정렬된 스트림에서 findFirst를 사용하자.

    Optional<Dish> dish = menu.stream()
        .sorted(Comparator.comparing(Dish::getCalories))
        .filter(Dish::isVegetarian)
        .findFirst();

     

     

     

     

    리듀싱

    스트림의 모든 요소를 반복해서 처리하고, 하나의 값으로 도출하는 것을 리듀싱 연산이라고 한다.

    int sum = 0;
    for(int x : numbers){
    	sum = sum + x;
    }

    위 코드를 reduce를 이용해 표현하면 아래와 같다

    초기값은 0으로 시작하고, numbers의 값을 하나씩 꺼내  더한다

    int sum = numbers.stream().reduce(0, (a, b) -> a + b);
    int sum = numbers.stream().reduce(0, Integer::sum);

     

    reduce로 합계를 구하는것과, 기존 외부반복의 합계를 구하는 것에는 어떤 차이가 있을까?

     

    외부반복을 이용하면 sum을 공유해야하므로 쉽게 병렬화하기 어렵지만

    reduce를 이용하면 내부반복이 추상화되면서 내부구현에서 병렬로 reduce를 실행할 수 있게된다

     

     

     

     

    기본형 특화 스트림

    int calories = menu.stream()
    	.map(Dish::getCalories)
    	.reduce(0, Integer::sum)

    위 코드에는 Integer를 int로 변환하는 박싱 비용이 숨어있다.

    스트림은 숫자 스트림을 효율적으로 처리할 수 있도록 IntStream, DoubleStream, LongStream을 제공한다.

    기본형 특화 스트림은 오직 박싱 과정에서 일어나는 효율성과 관련있으며, 

    스트림에 추가기능을 제공하지 않는다.

     

     

    일반 스트림 -> 특화 스트림

    스트림을 특화 스트림으로 변환할때는 mapToInt, mapToDouble, mapToLong을 가장 많이 사용한다.

    int calories = menu.stream()
    	.mapToInt(Dish::getCalories)    //IntStream 반환
    	.sum();

     

    특화 스트림 -> 일반 스트림

    특화스트림을 다시 객체 스트림으로 변환하고자 할때는 boxed메서드를 사용한다.

    IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
    
    Stream<Integer> stream = intStream.boxed();

     

     

     

     

    다양한 스트림 만들기

    정적인 스트림 만들기 Stream.of

    Stream<String> stream = Stream.of("Modern", "Java", "In", "Action");

     

    빈 스트림 만들기 Stream.empty()

    Stream<String> emptyStream = Stream.empty();

     

    Null일 수 있는 스트림 만들기 Stream.ofNullable()

    Stream<String> valueStream = Stream.ofNullable(System.getProperty("home"));

     

    배열로 스트림 만들기 Arrays.stream()

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

     

     

     

     

     

    무한 스트림

    크기가 고정되지 않은 스트림을 만들 수 있다.

    보통 무한한 값을 출력하지 않도록 limit와 함께 사용한다.

     

     

    무한 스트림 만들기 - iterate

    초기값 0, 이전결과에 2씩 더하며 무한스트림을 생성하는 코드이다.

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

     

    피보나치 수열 집합을 만드는 코드이다.

    (0, 1), (1, 1), (1, 2), (2, 3), (3, 5), (5, 8)....

    Stream
    	.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0]+t[1]})
    	.limit(10)
    	.map(t -> t[0])
    	.forEach(System.out::println);		//0, 1, 1, 2, 3, 5, 8, ...

     

     

    무한 스트림 만들기 - generate

    iterate와 달리 generate는 생산된 값을 연속적으로 계산하지 않고, Supplier<T>를 인수로 받아 새로운 값을 생산한다.

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

     

     

     

     

     

     

     

     

     

    #모던액션인자바 #자바 #자바8 #자바스트림 #스트림 #스트림활용 #필터링 #슬라이싱 #요소검색 #스트림필터링 #스트림슬라이싱 #스트림맵핑 #스트림평면화 #스트림일치확인 #스트림리듀싱 #기본형특화스트림 #정적스트림 #빈스트림 #무한스트림

     

     

    반응형
    • 네이버 블러그 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 구글 플러스 공유하기
    • 카카오톡 공유하기