stream

한상우·2023년 5월 31일
0

java

목록 보기
14/16

stream

Stream이란


컬렉션의 저장 요소를 하나씩 참조해서 람다식으로 참조할 수 있는 반복자

기존에는 많은 양의 데이터를 다룰 때 배열이나 컬렉션에 데이터를 담고 for문과 Iterator를 돌림

but, 재사용성이 떨어지고 코드 길어짐

Stream 특징


  1. 람다식으로 처리

    public static void main(String[] args) {
    
            List<Integer> numList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
            Stream<Integer> carStream = numList.stream();
            
            carStream.forEach(s -> {
                System.out.println(s);
            });
        }
  2. 원본의 데이터를 변경하지 않는다.

    • 원본의 데이터를 조회하고 별도의 요소들로 Stream을 생성
  3. 내부 반복자를 사용하여 병렬 처리가 쉽다

    • 외부 반복자: 개발자가 코드로 직접 컬렉션의 요소를 반복해서 가져옴 (for, while)
    • 내부 반복자: 요소 반복은 컬렉션에게 맡기고 개발자는 처리에만 집중 내부 반복자는 요소들의 반복 순서를 변경하거나, 멀티 코어 CPU를 최대한 활용하기 위해 요소들을 분배시켜 병렬 작업을 할 수 있게 함 (각 컬렉션 요소의 합을 구할 때 병렬 처리를 안하면 처음부터 더해서 값을 구하겠지만, 병렬 처리를 하면 여러 스레드가 요소들을 부분적으로 합하고 최종 합을 도출)
  4. 중간 처리와 최종 처리가 가능

    • 반복문이 forEach 라는 함수 내부에 있음

Stream 파이프 라인


대량의 데이터를 가공해서 축소하는 것을 reduction이라고 한다 (평균값, 최대값, 합계 … )

이러한 리덕션을 바로 할 수 없을 때 집계하기 좋도록 중간 처리가 필요

파이프라인은 여러개의 스트림이 연결되어 있는 형태로 최종 처리를 제외하고는 모두 중간 처리 스트림이다.

Stream 인터페이스는 많은 중간처리 메소드가 있는데, 이 메소드들은 중간처리 된 스트림을 반환한다.

List<Integer> numList = new ArrayList<>(Arrays.asList(1, 2, 3, 8, 4, 3, 3, 6, 9));
        Stream<Integer> carStream = numList.stream();

        Optional<Integer> first = carStream
                .distinct() // 중간처리
                .filter(x -> x > 4) // 중간처리
                .sorted() // 중간처리
                .findFirst(); // 최종 처리
  • 중간처리, 최종처리 메소드가 따로 있다.

중간 처리


Filter

Distinct

매핑

  • map
- 요소를 대체하는 스트림을 생성할 때
  • flatMap

    • 기존 요소 보다 많은 복수개의 스트림을 생성할 때 사용
public static void main(String[] args) {

        List<String> inputList = Arrays.asList("hello java", "hello spring");
        Stream<String> stream = inputList.stream().map(s -> s.toUpperCase());
        System.out.println(stream.toList()); // [JAVA, SPRING]

        Stream<String> stream = inputList.stream().flatMap(data -> Arrays.stream(data.split(" ")));
        System.out.println(stream.toList()); // [hello, java, hello, spring]
    }

Sorted

Peek

스트림에 영향을 주지 않으면서 특정 연산을 수행할 때 (ex. 중간에 출력을 하고 싶을 때)

최종 처리


Max, Min, Sum, Average, Count

Collect

stream의 요소들을 List, Set, Map 등 다른 종류의 결과로 수집할 때 사용

Collector 타입을 인자로 받아 처리한다.

  • Collectors.toList()
    List<String> inputList = Arrays.asList("hello java", "hello spring");
            List<String> collect = inputList.stream().map(s -> s.toUpperCase()).collect(Collectors.toList());
            System.out.println(collect);
  • Collectors.joining()
    • 작업한 결과를 하나의 String으로 이어 붙임
  • Collectors.groupingBy()

allMatch, anyMatch, noneMatch

특정한 조건을 충족하는지 검사, boolean으로 반환

List<String> inputList = Arrays.asList("hello java", "hello spring");
        boolean match = inputList.stream().map(s -> s.toUpperCase()).anyMatch(name -> name.contains("HELLO"));
        System.out.println(match);

forEach

주의사항


스트림은 재사용할 수 없다

for-loop 보다 느리다고 함

스트림을 사용하면서 람다나 메서드 참조를 사용하는 경우에는 지역 변수를 사용할 수 없다.

Stream 동작 순서


Arrays.stream(new String[]{"c", "python", "java"})
                .filter(word -> {
                    System.out.println("Call filter method: " + word);
                    return word.length() > 3;
                })
                .map(word -> {
                    System.out.println("Call map method: " + word);
                    return word.substring(0, 3);
                }).findFirst();

이와 같은 상황에서 출력 결과를 보면

Call filter method: c
Call filter method: python
Call map method: python

이렇게 나온다. 왜냐.!! 각 스트림 중간 연산 조건에 만족하면 바로 밑으로 내려가기 때문에 그리고 최종 연산이 하나만 찾는 것이기 때문에 java는 가지도 않는다. (만약 최종 연산이 그냥 출력으로 바뀌었으면 python 이후에 java도 출력될것이다)

위와 같은 스트림 동작 순서를 알고 있으면 스트림 동작을 최소화 시켜 성능 개선에 도움이 될것이다!

profile
안녕하세요 ^^

0개의 댓글