Stream

Suyeon Jin·2022년 2월 6일
0

더 자바, Java 8

목록 보기
1/2
post-custom-banner

> 컬렉션이 데이터를 가지고 있는 저장소라면,
스트림은 이 데이터를 가지고 원하는 방식으로 처리해주는 컨베이어 벨트라고 할 수 있다.
  • sequence of elements supporting sequential and parallel aggregate operations

  • Stream은 연속된 데이터를 처리하는 오퍼레이션들의 모임이다.

  • 무제한으로 데이터가 들어와도 처리가 가능하다.
    - Short Circuit 메소드를 사용하여 들어오는 데이터를 제한할 수 있다.

  • Functional in nature, 스트림이 처리하는 데이터 소스는 변경하지 않는다. (원본 변경 x)

  • 즉, 스트림으로 전달받은 데이터 자체를 변경하지 않는다.

List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");

// list에 저장된 데이터를 대문자로 바꾸는 처리를 하는 stream
Stream<String> stringStream = list.stream()
								.map(String::toUpperCase); // stream을 리턴

// list 객체 안의 기존 데이터는 변경되지 않음
list.forEach(System.out::println); // apple banana

1. Stream pipeline

  • 오퍼레이션들의 집합
  • 0 또는 다수의 중개 오퍼레이션1개의 종료 오퍼레이션으로 구성되어 있다.
  • 중개 오퍼레이션은 종료 오퍼레이션을만나지 않으면 실행되지 않는다.
    - 반드시 하나의 종료 오퍼레이션이 있어야 한다.

1-1) Intermediate operation (중개 오퍼레이션)

  • Stream을 리턴한다.
  • 연산을 마치고 처리를 위한 다음 연산을 진행할 수 있다.
  • 근본적으로 lazy하다.
    - lazy? 종료 오퍼레이션과 만나기 전까지 실행하지 않는다.
  • ex) filter, map, limit, sorted ...
// 종료 오퍼레이션을 만나지 못했으므로, 코드가 실행되지 않음
// 단순히 스트림을 정의한 것뿐 실행은 안됨

list.stream().map((s) -> {
		System.out.println(s); // 출력되지 않음
        return s.toUpperCase();
});

1-2) Terminal operation (종료 오퍼레이션)

  • Stream을 리턴하지 않는다.
  • 연산을 마치고 해당 처리를 종료한다.
  • ex) collect, count, forEach, min, max ...
List<String> collect = list.stream().map((s) -> {
		System.out.println(s); // 소문자로 출력됨
        return s.toUpperCase(); // 대문자로 변경됨
}).collect(Collectors.toList()); // list 객체로 반환됨

collect.forEach(System.out::println); // 대문자로 출력됨

2. parallelStream

  • 스트림은 병렬처리를 쉽게 할 수 있다.
  • 병렬처리가 항상 좋은 경우는 아니다.
    - thread 생성 비용, 병렬처리 후 수집 비용, 컨텍스트 스위칭 비용 등등 ...
  • 데이터가 방대하게 큰 경우 사용하면 유용하다.
  • 직접 성능을 측정해보고 결정하는 것이 좋다!
List<String> parallelCollect = list.parallelStream().map((s) -> {
            System.out.println(s + " , <Thread name> " + Thread.currentThread().getName());
            return s.toUpperCase();
        }).collect(Collectors.toList());

        parallelCollect.forEach(System.out::println);

3. Stream API

3-1) 걸러내기

  • Filter(Predicate)
  • 스트림에서 특정 조건을 만족하는 데이터만 걸러서 만든 새로운 스트림으로 반환한다.
  • 중개 오퍼레이션
  • ex) 수업 이름에 java가 들어가는 데이터만 스트림으로 만들기

3-2) 변경하기

  • Map(Function) , FlatMap(Function)
  • 하나의 데이터 안에서 특정 값만 꺼내거나 변경하여 새로운 스트림으로 반환한다.
  • flatMap은 여러 개의 값으로 감싸져있는 데이터(Array, List)를 세분화하여 하나로 합친 새로운 스트림을 생성한다.
  • 하나의 input에 여러 개의 output이 나올 때, flatMap을 사용한다.
  • 중개 오퍼레이션
  • ex) 수업 이름을 의미하는 String 객체만을 갖는 스트림으로 만들기

3-3) 생성하기

  • generate(Supplier) , Iterator(T seed, UnaryOperator)
  • seed로부터 UnaryOperator를 무제한으로 반복하는 스트림으로 반환한다.
  • 중개 오퍼레이션
  • ex) 10부터 1씩 증가하는 무제한 스트림 만들기

3-4) 제한하기

  • limit(long) , skip(long)
  • 중개 오퍼레이션
  • ex) 최대 10개의 데이터가 담긴 스트림 만들기
  • ex) 앞 5개를 제외한 나머지 데이터가 담긴 스트림 만들기

3-5) 스트림에 있는 데이터가 특정 조건을 만족하는지 확인

  • anyMatch() , allMatch() , nonMatch()
  • 스트림의 데이터들을 돌며 특정 조건을 만족하는지 확인한다.
  • 종료 오퍼레이션
  • ex) 스트림에 있는 모든 데이터들이 5보다 큰지 확인한다. (boolean 값으로 반환된다.)

3-6) 개수 세기

  • count()
  • 종료 오퍼레이션
  • ex) 10보다 큰 데이터의 개수를 반환한다.

3-7) 스트림을 데이터 하나로 뭉치기

  • reduce(identity, ByFunction) , collect() , sum() , min() , max()
  • 종료 오퍼레이션
  • ex) 모든 숫자의 합 구하기
  • ex) 모든 데이터를 하나의 리스트 혹은 Set에 옮겨 담기

예제

// setter, getter, 생성자는 만들어져있다고 가정
public class OnlineClass {
    private Integer id;
    private String title;
    private boolean closed;
}
public class App {

    public static void main(String[] args) {
        List<OnlineClass> springClasses = new ArrayList<>();
        springClasses.add(new OnlineClass(1, "spring boot", true));
        springClasses.add(new OnlineClass(2, "spring data jpa", true));
        springClasses.add(new OnlineClass(3, "spring mvc", false));
        springClasses.add(new OnlineClass(4, "spring core", false));
        springClasses.add(new OnlineClass(5, "rest api development", false));

        List<OnlineClass> javaClasses = new ArrayList<>();
        javaClasses.add(new OnlineClass(6, "The Java, Test", true));
        javaClasses.add(new OnlineClass(7, "The Java, Code manipulation", true));
        javaClasses.add(new OnlineClass(8, "The Java, 8 to 11", false));

        List<List<OnlineClass>> events = new ArrayList<>();
        events.add(springClasses);
        events.add(javaClasses);

        System.out.println("spring 으로 시작하는 수업");
        springClasses.stream()
                .filter(onlineClass -> onlineClass.getTitle().startsWith("spring")) // 중개 오퍼레이션
                .forEach(onlineClass -> System.out.println(onlineClass.getTitle())); // 종료 오퍼레이션

        System.out.println("close 되지 않은 수업");
        springClasses.stream()
                .filter(onlineClass -> !onlineClass.isClosed()) // 중개 오퍼레이션
                .forEach(onlineClass -> System.out.println(onlineClass.getTitle())); // 종료 오퍼레이션

        System.out.println("수업 이름만 모아서 스트림 만들기");
        springClasses.stream()
                .map(OnlineClass::getTitle) // 중개 오퍼레이션 (OnlineClass -> String)
                .forEach(System.out::println); // 종료 오퍼레이션 (String 출력)

        System.out.println("두 수업 목록에 들어있는 모든 수업 아이디 출력");
        events.stream()
                .flatMap(Collection::stream) // 스트림 input으로 들어온 List를 flatten시켜 OnlineClass 객체로 만든다.
                .forEach(onlineClass -> System.out.println(onlineClass.getId())); // 종료 오퍼레이션

        System.out.println("10부터 1씩 증가하는 무제한 스트림 중에서 앞에 10개 빼고 최대 10개 까지만");
        Stream.iterate(10, i -> i+1) // 증가 스트림 생성
                .skip(10) // 앞에 10개 제외
                .limit(10) // 최대 10개
                .forEach(System.out::println); // 종료 오퍼레이션

        System.out.println("자바 수업 중에 Test가 들어있는 수업이 있는지 확인");
        boolean result = javaClasses.stream()
                .anyMatch(onlineClass -> onlineClass.getTitle().contains("Test")); // boolean으로 반환
        System.out.println(result);

        System.out.println("스프링 수업 중에 제목에 spring이 들어간 것만 모아서 List로 만들기");
        List<String> list = springClasses.stream()
                .map(OnlineClass::getTitle) // 제목만 모아서 새로운 stream 객체를 생성
                .filter(title -> title.contains("spring")) // 조건에 맞게 거름
                .collect(Collectors.toList()); // 처리한 스트림을 List 객체로 반환
        list.forEach(System.out::println);

    }
}

이 포스팅은 백기선님의 더 자바, Java 8을 수강하고 정리한 내용입니다.

post-custom-banner

0개의 댓글