ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java - 스트림(Stream) 중간 처리 메서드의 종류와 사용 방법
    Language/Java 2022. 11. 30. 17:55

    스트림(Stream) 중간 처리 메서드의 종류와 사용 방법

    중간 처리 메서드의 종류

     

    위 표는 중간 처리 메서드의 종류를 나타낸 것이다. 위와 같이 리턴 타입이 스트림이라면 중간 처리 메서드이다. 또한, 소속된 인터페이스가 공통이라는 의미는 Stream, IntStream, LongStream, DoubleStream에서 모두 제공된다는 뜻이다.

    종류로는 필터링, 매핑, 정렬, 루핑이 있는데 하나씩 살펴보도록 하겠다.

     

    필터링

    필터링은 중간 처리 기능으로 요소를 걸러내는 역할을 한다. 필터링 메서드에는 distinct()와 filter()가 있다. 이들은 모든 스트림이 가지고 있는 공통 메서드이기도 하다.

     

    1. distinct()

    distinct() 메서드는 중복을 제거하는데, Stream의 경우 Object.equals(Object)가 true이면 동일한 객체로 판단하여 중복을 제거하고, 나머지 IntStream, LongStream, DoubleStream은 동일 값일 경우 중복을 제거한다.

     

    public class Main {
        public static void main(String[] args) {
            List<String> names = Arrays.asList("홍길동", "이순신", "임꺽정", "홍길동", "이강인", "이순신");
            names.stream()
                    .distinct()
                    .forEach(name -> System.out.println(name));
        }
    }

    위 코드는 distinct() 메서드를 사용한 예시이다.

     

    홍길동
    이순신
    임꺽정
    이강인

    실행결과는 위와 같다.

     

    2. filter()

    filter() 메서드는 원하는 기준으로 요소를 걸러내는 역할을 한다. 여기서 원하는 기준은 Predicate 형태를 매개 값으로 주면 된다. 그러면 Predicate가 true를 리턴하는 요소만 필터링하는 방식으로 작동된다.

     

    public class Main {
        public static void main(String[] args) {
            List<String> names = Arrays.asList("홍길동", "이순신", "임꺽정", "홍길동", "이강인", "이순신");
            names.stream()
                    .distinct()
                    .filter(name -> name.equals("임꺽정"))
                    .forEach(name -> System.out.println(name));
        }
    }

    위 코드는 "임꺽정"이라는 이름만 필터링해서 출력하는 예제이다.

     

    임꺽정

    실행결과는 위와 같다.

     

    매핑

    매핑은 스트림의 요소를 다른 요소로 대체하는 작업을 의미한다. 스트림에서 제공하는 매핑 메서드는 flatMapXXX()와 mapXXX() 그리고 asDoubleStream(), asLongStream(), boxed()가 있다.

     

    1. flatMapXXX()

    flatMapXXX() 메서드는 Array나 Object로 감싸져 있는 모든 원소를 단일 원소 스트림으로 변환하는 역할을 하며, 매개변수로 Function을 사용한다. 그리고 내부적으로 T를 Stream<R> 형식인 스트림 형태로 매핑한다.

     

    public class Main {
        public static void main(String[] args) {
            List<String> inputList = Arrays.asList("Hello", "World");
            long res = inputList.stream()
                    .flatMap(data -> Arrays.stream(data.split("")))
                    .count();
    
            System.out.println(res);
        }
    }

    위 소스코드는 flatMap을 사용한 예제 코드이다. inputList에는 "Hello"와 "World"를 원소로 갖는다. 그리고 stream() 메서드를 통해 오리지널 스트림을 얻어오고 그 안에서 flatMap() 메서드를 사용하는 것을 알 수 있다.

    현재 스트림의 요소로는 "Hello"와 "World"가 있는데, 각각 5개의 단일 원소 스트림으로 나뉘게 된다. 그래서 스트림 안에는 10개의 요소가 존재하게 된다.

     

    10

    실행 결과는 위와 같다.

     

    2. mapXXX()

    mapXXX() 메서드는 단일 스트림의 원소를 매핑시킨 후 매핑시킨 값을 다시 스트림으로 변환하는 역할을 하며, 매개변수로 Function이나 Operator를 사용한다. 그리고 내부적으로 T를 R 형식으로 매핑하는데 flatMapXXX()과 다르게 스트림 외에 다른 형식으로 매핑이 가능하다.

     

    public class Main {
        public static void main(String[] args) {
            List<String> inputList = Arrays.asList("Hello", "World");
            long res = inputList.stream()
                    .flatMap(data -> Arrays.stream(data.split("")))
                    .count();
    
            System.out.println(res);
    
            res = inputList.stream()
                    .map(data -> Arrays.stream(data.split("")))
                    .count();
    
            System.out.println(res);
        }
    }

    flatMapXXX() 메서드와의 차이점을 확인하기 위해 위에서 사용한 코드의 flatMap() 메서드를 map() 메서드로만 바꿔보았다.

     

    10
    2

    출력 결과는 10, 2가 나온다. 왜 이러한 결과 나오는지 그림으로 확인해 보겠다.

     

     

    위 그림에서 두 번째 부분을 보면 알 수 있듯이, "Hello"가 5개의 요소로 쪼개지긴 하지만 그 요소들이 한꺼번에 스트림으로 묶여있다. 반면, 세 번째 줄을 보면 "Hello"가 5개의 단일 원소 스트림으로 쪼개지는 것을 확인할 수 있다.

    이것이 flatMapXXX() 메서드와 mapXXX() 메서드의 큰 차이점이라고 볼 수 있다.

     

    지금까지는 flactMapXXX() 메서드와 mapXXX() 메서드의 차이점을 중점적으로 설명하였고, 이번에는 mapXXX() 메서드를 사용한 예제를 살펴보겠다.

     

    public class Main {
        public static void main(String[] args) {
            List<Member> memberList = Arrays.asList(
                    new Member("홍길동", Gender.MALE, 22),
                    new Member("이순신", Gender.MALE, 30),
                    new Member("임꺽정", Gender.MALE, 25)
            );
    
            memberList.stream()
                    .mapToInt(member -> member.getAge())
                    .forEach(age -> System.out.println(age));
        }
    }

    Member 클래스의 멤버 변수로는 이름, 성별, 나이가 있는데, Member 객체의 나이를 출력하는 예제이다. mapToInt() 메서드를 이용하여 Member를 int에 매핑하는 것을 확인할 수 있다.

     

    22
    30
    25

    실행 결과는 위와 같다.

     

    3. asDoubleStream(), asLongStream(), boxed()

    asDoubleStream() 메서드는 IntStream의 int 요소 또는 LongStream의 long 요소를 double 요소로 타입 변환을 해서 DoubleStream을 생성한다.

    마찬가지로, asLongStream() 메서드는 IntStream의 int 요소를 long 요소로 타입 변환을 한 후 LongStream을 생성한다.

    boxed() 메서드는 int, long, double 요소를 Integer, Long, Double 요소로 박싱 해서 Stream을 생성한다.

    여기서 주의해야 할 점은 IntStream과 Stream<Integer>은 엄연히 다른 스트림이라는 것이다.

     

    public class Main {
        public static void main(String[] args) {
            int[] intArr = {1, 2, 3, 4, 5};
    
            IntStream intStream = Arrays.stream(intArr);
            intStream
                    .asDoubleStream()   // DoubleStream 생성
                    .forEach(data -> System.out.println(data));
    
            System.out.println();
    
            intStream = Arrays.stream(intArr);
            intStream
                    .boxed()   // Stream<Integer> 생성
                    .forEach(obj -> System.out.println(obj.intValue()));
        }
    }
    

    위 소스코드는 asDoubleStream() 메서드와 boxed() 메서드를 사용한 간단한 예제이다.

     

    1.0
    2.0
    3.0
    4.0
    5.0
    
    1
    2
    3
    4
    5

    실행 결과는 위와 같다.

     

    정렬

    스트림은 중간 처리 단계에서 요소를 일정한 기준에 맞게 정렬을 할 수 있다. 정렬을 할 수 있는 스트림의 타입은 Stream<T>과 IntStream, DoubleStream, LongStream이 있다.

     

    1. Stream<T>

    sorted() 메서드를 사용할 경우 객체를 Comparable 인터페이스의 구현 방법에 따라 결정한다. Student 클래스가 있다고 하면, Comparable 인터페이스를 구현하는 방식으로 compareTo() 메서드를 오버라이딩함으로써 정렬하는 기준을 정할 수 있다.

     

    반면, sorted(Comparator<T>)를 사용하였을 경우 객체를 주어진 Comparator에 따라 정렬한다.

     

    public class Main {
        public static void main(String[] args) {
            List<Member> memberList = Arrays.asList(
                    new Member("홍길동", Gender.MALE, 27),
                    new Member("이순신", Gender.MALE, 20),
                    new Member("임꺽정", Gender.MALE, 25)
            );
    
            memberList.stream()
                    .sorted((m1, m2) -> m1.getAge() - m2.getAge())
                    .forEach(m -> System.out.println(m.getName()));
        }
    }

    Member의 나이를 기준으로 오름차순 정렬을 하고, Member의 이름을 출력하는 예제이다.

     

    이순신
    임꺽정
    홍길동

    실행 결과는 위와 같다.

     

    2. IntStream, DoubleStream, LongStream

    이들은 sorted() 메서드를 사용하여 요소들을 오름차순으로 정렬할 수 있다. 만약, 매개변수를 통하여 정렬하는 기준을 설정해 주고 싶다면, 위에서 설명한 대로 Comparator를 정의하면 된다.


    출처

    https://steady-coding.tistory.com/313

     

    728x90

    댓글

Designed by Tistory.