Study/Java

자바 기본서를 다시 읽다. 8 - 스트림의 중간처리 메소드

going.yoon 2022. 4. 6. 21:24

스트림은 기본적으로 중간처리 메소드와 최종처리 메소드로 이루어진 파이프라인으로 활용할 수 있다고 이전에 알아보았다.

그렇다면 중간처리와 최종 처리시 각각 활용할 수 있는 메소드들에 어떤 것들이 있는지 알아보자.

 

스트림의 중간처리 메소드

distinct(), filtering()

첫번째로, 중복 데이터를 걸러내는 distinct(), 특정 기준으로 데이터를 걸러주는 filtering()에 대해 알아보자.

List<String> list = Arrays.asList("윤가영","김나영","윤다영","김라영","윤마영","윤가영");
list.stream()
        .distinct() // 중복제거
        .filter(n-> n.contains("윤")) // 조건
        .forEach(s-> System.out.println("name is : " + s));

실행 결과

 

flapMapXXX(), mapXXX()

두번째로는 요소를 대체하는 복수개의 요소들로 구성된 새로운 스트림을 리턴하는 flapMapXXX() 메소드와 mapXXX() 이다.

public class IterationStreamExample {
    public static void main (String[] args){

        /**1. flatMap example*/
        List<String> list = Arrays.asList("java8 lamda","stream mapping");
        list.stream()
                .flatMap(data -> Arrays.stream(data.split(" ")))
                .forEach(word -> System.out.println("word is : " + word));

        /**2. flatMapToInt example*/
        List<String> inputList2 = Arrays.asList("10,20,30" , "40,50,60");
        inputList2.stream()
                .flatMapToInt(data -> {
                    String[] strArr = data.split(",");
                    int[] intArr = new int[strArr.length];
                    for(int i = 0; i < strArr.length ; i++){
                        intArr[i] = Integer.parseInt(strArr[i]);
                    }
                    return Arrays.stream(intArr);
                })
                .forEach(word -> System.out.println(word));


        /**3. Map example*/
        List<Student> students = Arrays.asList(
                new Student("윤가영", 100, 100),
                new Student("윤나영", 30, 30)
        );

        students.stream()
                .mapToInt(Student::getEnglishScore)
                .forEach( s-> System.out.println("학생들의 영어 성적 : " +s ));

    }

}

실행결과

 

asDoubleStream(), asLongStream(), boxed()

세번째로는 asDoubleStream(), asLongStream(), boxed() 메소드이다. 

  • asDoubleStream() 의 역할: intStream to DoubleStream, LongStream to DoubleStrim 변환
  • asLongStream() 의 역할: intStream to LongStream
  • boxed() 의 역할: intStream to Integer, longStream to Long, doubleStream to Double
public static void main (String[] args){

    int[] intArray = {1,2,3,4,5};

    IntStream intStream = Arrays.stream(intArray);
    /** 1. asDouble로 int 요소들을 double로 변환*/
    intStream.asDoubleStream().forEach(s-> System.out.println("double val : " + s));

    /** 2. boxed로 int 요소들을 Integer로 변환*/
    intStream = Arrays.stream(intArray); // Cf. intStream 재선언
    intStream.boxed().forEach(s-> System.out.println(s.getClass()));

}

소스코드를 실행해본 결과 int 값들이 double과 Integer로 잘 변환됨을 확인하였다. 

실행결과

다만 cf. intStream 재선언 부분을 보자면, 스트림이 한번 선언되고 iteration을 돌고 나면, 다시 선언해주어야 한다. 그렇지 않고 intStream을 재사용 했다가는 다음과 같은 stream has already been operated upon or closed라는 에러가 뜬다.

IterationStreamExample

 

 

 

sorted()

네번째로는 sorted() 메소드이다. sorted는 최종처리가 아니라 중간처리 스트림이다. 이 sorted를 사용하는 방법은 크게 두가지로 나뉘는데, 요소가 Comparable을 구현하고 있는 경우와, 구현하고 있지 않은 경우로 나눌 수 있다.

 

먼저 요소객체가 Comparable을 구현하고 있는 경우에는 sorted()를 아래와 같이 호출한다.

// 1. compareTo에 구현된대로 sorting
sorted();
sorted( (a,b) -> a.compareTo(b));
sorted( (Comparator.naturalOrder());

// 2. compareTo에 구현된 것과 반대로 sorting
sorted( (a,b) -> b.compareTo(a));
sorted( (Comparator.reverseOrder());
public class IterationStreamExample {

    public static class Car implements Comparable<Car> {
        String name;
        int price;

        public Car (String name, int price){
            this.name = name;
            this.price = price;
        }

        @Override
        public int compareTo(Car o1) {
            return Integer.compare(price, o1.price);
        }
    }

    public static void main (String[] args){

        /**1. 요소 : int , compareTo 구현여부 : O */
        int[] intArray = {3,5,2,4,1};
        IntStream intStream = Arrays.stream(intArray);
        intStream.sorted().forEach(s-> System.out.print(s + ","));

        System.out.println("\r\n");

        /**2. 요소 : car , compareTo 구현여부 : O*/
        List<Car> carList = Arrays.asList(
                new Car("소나타",2000000)
                , new Car("중나타" , 3000000)
                , new Car("낙타", 400000)
        );

        carList.stream().sorted( (a,b) -> b.compareTo(a)).forEach(s-> System.out.println(s.name+" : " + s.price));
    }

}

실행결과

 

만약 요소 객체가 comparable을 구현하고 있지 않은 경우에는, 아래와 같이 Comparator의 comparing method를 통해 비교한다.

public class IterationStreamExample {

    public static class Car{
        String name;
        int price;

        public Car (String name, int price){
            this.name = name;
            this.price = price;
        }

        public int getPrice() {return this.price;}
    }

    public static void main (String[] args){

        /**1. 요소 : Car , compareTo 구현여부 : X */
        List<Car> carList = Arrays.asList(
                new Car("조비" , 10000) ,
                new Car("조눈" , 20000) ,
                new Car("믿거조" , 30000)
        );

        carList.stream()
                //Comparator의 comparing 메소드 호출.
                .sorted(Comparator.comparing(Car::getPrice).reversed())
                .forEach(s-> System.out.println(s.name+"," + s.price));
    }

}

실행 결과

 

 

peek()

마지막으로 peek()에 대해 알아보자. 위의 예시에서 수도없이 forEach() 메소드를 써왔다. forEach는 최종처리 메소드로써, 모든 중간처리 스트림이 종료 된 이후에 사용해왔는데, 만약 중간처리 스트림을 처리하는 도중 전체 요소를 looping하면서 처리를 해야한다면 어떻게 해야될까? 이때 사용하는 메소드가 바로 peek() 이다.

 

peek을 호출하고 최종 처리 메소드를 호출하지 않으면 지연이 되기 때문에 꼭 최종처리 메소드를 호출해주어야 한다.

public static void main (String[] args){

    IntStream intStream = Arrays.stream(new int[]{3,4,2,1});
    intStream.filter(a->a%2==0)
            .peek(s-> System.out.println(s)) // 4와 2만 출력
            .sum();


}