elevne's Study Note

Java - Stream 본문

Backend/Java

Java - Stream

elevne 2023. 5. 19. 15:21

Stream 은 Java 8 부터 추가된 컬렉션 (+ 배열) 의 저장 요소를 하나씩 참조해서 Functional Style 로 처리할 수 있도록 해주는 반복자이다. Stream 은 Iterator 과 비슷한 역할을 하는 반복자이지만, 람다식으로 요소 처리 코드를제공하는 점과 내부 반복자를 사용하므로 병렬처리가 쉽다는 점, 중간처리와 최종처리 작업을 수행할 수 있다는 점에서 차이가 있다. java.util.Stream 패키지에서 Stream API 들을 확인해볼 수 있다. 

 

 

 

Stream 은 데이터의 필터링, 매핑, 그루핑, 정렬 등의 중간처리와 합계, 평균, 카운팅, 최대값, 최소값 등의 최종처리를 파이프라인으로 해결한다. 파이프라인은 여러 개의 스트림이 연결되어 있는 구조를 말한다. 

 

 

 

 

 

 

스트림이 생성될 때 바로 중간처리 (필터링, 매핑 등) 가 되는 것이 아니라 최종처리가 시작되기 전까지 중간처리는 지연된다 (LAZY). 최종처리가 시작되면 비로소 컬렉션의 요소가 하나씩 중간스트림에서 처리되고 최종처리까지 오게되는 것이다. 아래와 같은 예제 코드를 작성해볼 수 있다.

 

 

 

public class Member {
    public static int MALE = 0;
    public static int FEMALE = 1;

    private String name;
    private int sex;
    private int age;

    public Member(String name, int sex, int age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public int getSex() {
        return sex;
    }
}

 

 

public class StreamPipelineExample {

    public static void main(String[] args) throws Exception {
        List<Member> list = Arrays.asList(
                new Member("A", Member.MALE, 30),
                new Member("B", Member.FEMALE, 31),
                new Member("C", Member.MALE, 32),
                new Member("D", Member.FEMALE, 33),
                new Member("E", Member.FEMALE, 34)
        );
        double ageAvg = list.stream()
                .filter(m -> m.getSex()==Member.FEMALE)
                .mapToInt(Member::getAge)
                .average()
                .getAsDouble();

        System.out.println(ageAvg);
    }
}

 

 

result

 

 

 

Stream API 에서 사용할 수 있는 몇몇 중간처리, 최종처리 메서드들에 대해 알아보았다.

 

 

 

distinct()

 

distinct() 메서드는 중복을 제거해준다.

 

 

public static void main(String[] args) {
    List<String> list = Arrays.asList(
            "A", "B", "C", "D", "E", "F", "A"
    );
    list.stream().distinct().forEach(System.out::println);
}

 

 

result

 

 

 

filter()

 

filter() 메서드는 매객값으로 Predicate 를 받고, true 를 리턴하는 요소만 남긴다. 위 첫 번째 예시에서 사용되었다.

 

 

 

flatMapXXX(), mapXXX()

 

flatMapXXX() 는 요소를 대체하는 복수 개의 요소들로 구성된 새로운 스트림을 리턴한다. 아래와 같이 작성해볼 수 있는 것이다.

 

 

public static void main(String[] args) {
    List<String> list1 = Arrays.asList(
            "java8 lambda", "stream mapping"
    );
    list1.stream()
            .flatMap(data -> Arrays.stream(data.split(" ")))
            .forEach(System.out::println);

    List<String> list2 = Arrays.asList(
            "1,2,3,4", "5, 6"
    );
    list2.stream()
            .flatMapToInt(data -> {
                String[] arr1 = data.split(",");
                int[] intArr = new int[arr1.length];
                for (int i = 0; i < arr1.length; i++) {
                    intArr[i] = Integer.parseInt(arr1[i].trim());
                }
                return Arrays.stream(intArr);
            }).forEach(System.out::println);
}

 

 

result

 

 

위와 다르게 mapXXX() 메서드는 요소를 대체하는 요소로 구성된 새로운 스트림을 반환한다. 가장 첫 번째 예제 코드에서 mapToInt 를 확인할 수 있다.

 

 

 

sorted()

 

Stream 은 요소가 최종처리되기 전, 중간단게에서 미리 요소를 정렬해서 최종처리 순서를 변경할 수 있다. 이를 사용할 때는 객체를 Comparable 구현 방법에 따라 정렬하거나, 따로 주어진 Comparator 에 따라서 정렬할 수 있다. 

 

 

먼저, 객체 요소일 경우 클래스가 Comparable 을 구현하지 않으면 sorted() 메서드를 호출하였을 때 ClassCastException 이 발생하게 된다. 아래와 같이 Comparable 을 구현한다.

 

 

public class Member implements Comparable<Member> {

    @Override
    public int compareTo(Member other) {
        if (this.age > other.age) {
            return 1;
        } else if (this.age < other.age) {
            return -1;
        } else {
            return 0;
        }
    }
    ...

 

 

public static void main(String[] args) throws Exception {
    List<Member> list = Arrays.asList(
            new Member("A", Member.MALE, 30),
            new Member("B", Member.FEMALE, 31),
            new Member("C", Member.MALE, 32),
            new Member("D", Member.FEMALE, 33),
            new Member("E", Member.FEMALE, 34)
    );
    list.stream()
            .sorted().forEach(System.out::println);
}

 

 

result

 

 

 

allMatch(), anyMatch(), noneMatch()

 

Stream 클래스에서는 최종처리 단계에서 요소들이 특정 조건에 만족하는지 조사할 수 있도록 위 세 가지 매칭 메서드를 제공한다. allMatch() 는 모든 요소가 만족, anyMatch() 는 하나라도 만족, noneMatch() 는 하나도 만족되지 않는지에 대한 여부를 boolean 으로 반환한다.

 

 

 

sum(), count(), average(), max(), min()

 

집계, 최종처리 기능으로 요소들을 처리하여 카운팅, 합계, 평균값, 최대값, 최소값 등과 하나의 값으로 산출되는 것을 반환한다.

 

 

 

reduce()

 

Stream 의 커스텀 집계 기능이다. 아래와 같이 작성할 수 있다.

 

 

public static void main(String[] args) {
    IntStream intStream1 = Arrays.stream(new int[] { 1, 4, 3, 2});
    IntStream intStream2 = Arrays.stream(new int[] { 1, 4, 3, 2});
    int sum1 = intStream1.sum();
    System.out.println(sum1);
    int sum2 = intStream2.reduce((a, b) -> a + b).getAsInt();
    System.out.println(sum2);
}

 

 

result

 

 

 

 

 

 

Reference:

이것이 자바다

'Backend > Java' 카테고리의 다른 글

Java - 보조 스트림  (0) 2023.05.21
Java - IO 입출력  (0) 2023.05.20
Java - Collection  (0) 2023.05.17
Java - Multi Thread (4)  (0) 2023.05.14
Java - Multi Thread (3)  (0) 2023.05.13