Java/더 자바, Java 8

Stream 스트림

bongveloper 2023. 2. 28. 23:08

이번 시간에는 스트림에 대해서 알아보도록 하겠습니다.

먼저, 이 글은 백기선님 자바 8 강의를 듣고 스터디하는 글 임을 미리 알려드립니다!

 

1. Stream이란?
2. Stream 구현
3. Stream 문제 풀어보기

Stream 이란?

Java Stream(자바 스트림)은 자바 8에서 추가된 기능 중 하나로, 컬렉션(Collection)이나 배열 등의 데이터 소스에서 
요소를 처리하는 기능을 제공합니다.

스트림은 여러 개의 중간 오퍼레이션과 종료오퍼레이션으로 이루어져 있습니다.
중간 오퍼레이션은 스트림의 요소를 가공하여 새로운 스트림을 반환하고,
종료 오퍼레이션은 스트림의 요소를 소모하면서 최종 결과를 반환합니다.
이러한 스트림의 특징으로 인해, 다양한 데이터 처리 작업을 간결하고 가독성 높은 코드로 처리할 수 있습니다.

스트림을 사용하면 요소를 한 번에 하나씩 처리하는 반복문을 작성하지 않아도 되므로,
코드가 간결해지고 가독성이 좋아집니다. 또한, 병렬 처리가 쉽게 구현될 수 있기 때문에 대용량 데이터 처리에 유리합니다.
- Chat Gpt -

Chat Gpt에서 스트림에 대하여 이야기 하는 것 중 중요한 점을 보면

  • Java8에서 추가된 기능이며 컬렉션이나 배열등에서 사용된다.
  • 스트림은 중간 오퍼레이션과 종료 오퍼레이션으로 이루어져 있다.
  • 다양한 데이터 처리 작업을 간결하고 가독성 높은 코드로 처리할 수 있다.
  • 병렬처리가 된다.

스트림은 중간 오퍼레이션과 종료 오퍼레이션으로 이루어져 있는데

중간 오퍼레이션의 결과는 Stream이다.

 

중간 오퍼레이션의 종류

  1. filter(): 주어진 조건에 맞는 요소만을 선택하여 새로운 스트림을 반환합니다.
  2. map(): 요소들을 주어진 함수에 따라 변환하여 새로운 스트림을 반환합니다.
  3. flatMap(): 요소를 1대N으로 변환한 후, 모든 요소를 평면화하여 하나의 스트림으로 반환합니다.
  4. distinct(): 중복된 요소를 제거한 스트림을 반환합니다.
  5. sorted(): 스트림의 요소를 정렬하여 새로운 스트림을 반환합니다.
  6. limit(): 처음 N개의 요소만 선택하여 새로운 스트림을 반환합니다.
  7. skip(): 처음 N개의 요소를 생략하고, 나머지 요소들을 새로운 스트림으로 반환합니다.
  8. peek(): 스트림의 요소를 소비하지 않으면서, 요소의 상태를 확인할 수 있습니다.
  9. takeWhile(): 주어진 조건을 만족하는 요소만 선택하여 새로운 스트림을 반환합니다.
  10. dropWhile(): 주어진 조건을 만족하는 요소를 버리고, 나머지 요소들로 새로운 스트림을 반환합니다.

종료 오퍼레이션 종류

  1. forEach(): 스트림의 모든 요소를 소비하면서, 주어진 작업을 수행합니다.
  2. toArray(): 스트림의 모든 요소를 배열로 반환합니다.
  3. reduce(): 스트림의 요소를 하나의 값으로 줄이는 작업을 수행합니다.
  4. collect(): 스트림의 요소를 수집하여 새로운 컬렉션을 만듭니다.
  5. count(): 스트림의 요소 개수를 반환합니다.
  6. min(), max(): 스트림의 요소 중 최솟값 또는 최댓값을 반환합니다.
  7. anyMatch(), allMatch(), noneMatch(): 주어진 조건에 대해, 스트림의 요소 중 하나라도, 모두, 혹은 하나도 일치하지 않는 요소가 있는지 검사합니다.
  8. findFirst(), findAny(): 스트림의 첫 번째 요소 혹은 아무 요소를 반환합니다.
  9. toList(), toSet(): 스트림의 요소를 List나 Set으로 변환하여 반환합니다.
  10. toMap(): 스트림의 요소를 Map으로 변환하여 반환합니다.

종료 오퍼레이션 없이 중간 오퍼에이션만 있다면 실제 요소를 처리하지 않습니다. (이걸 Lazy하다라고 표현합니다.)

코드를 만약에

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println("--------------------------------------------------------");
numbers.stream().map(number -> {
    System.out.println(number);
    return number;
});
System.out.println("--------------------------------------------------------");

이렇게 작성한다면 결과는 이렇게 나올 것입니다.

--------------------------------------------------------
--------------------------------------------------------

stream은 종료 오퍼레이션이 있어야 실제요소를 처리합니다.

@Test
public void IntermediateOperations(){
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    System.out.println("--------------------------------------------------------");
    List<Integer> collect = numbers.stream().map(number -> {
        System.out.println(number);
        return number;
    }).collect(Collectors.toList());
    System.out.println("--------------------------------------------------------");
}
--------------------------------------------------------
1
2
3
4
5
6
7
8
9
10
--------------------------------------------------------

더 많은 정보는 아래 링크에서 확인 가능합니다.

https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html

 

Stream (Java Platform SE 8 )

A sequence of elements supporting sequential and parallel aggregate operations. The following example illustrates an aggregate operation using Stream and IntStream: int sum = widgets.stream() .filter(w -> w.getColor() == RED) .mapToInt(w -> w.getWeight())

docs.oracle.com

 

2. Stream 구현해보기

@Test
public void streamTest() {

    //initial List
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

    // filter 메소드로 짝수만 필터링
    List<Integer> evenNumbers = numbers.stream()
            .filter(n -> n % 2 == 0)
            .collect(Collectors.toList());
    System.out.println("====================================");

    // reduce 메소드로 요소들의 합 구하기
    int sum = numbers.stream().reduce(0, Integer::sum);
    System.out.println("====================================");
    System.out.println(sum);
    System.out.println("====================================");
    evenNumbers.forEach(System.out::println);
    System.out.println("====================================");
    
    //병렬 처리
    List<Integer> collect = numbers.parallelStream().map(n -> {
                System.out.println(Thread.currentThread().getName());
                return n / 100 * 100;
            })
            .collect(Collectors.toList());
}

코드를 보면

중개 오퍼레이션 filter로 짝수만 필터링

종료 오퍼레이션 reduce로 요소들 합 구하기

중개 오퍼레이션 paralleStream으로 병렬처리하기가 있습니다.

 

filter 문서를 보면

Stream<T> filter(Predicate<? super T> predicate)
Returns a stream consisting of the elements of this stream that match the given predicate.
This is an intermediate operation.
Returns a stream consisting of the elements of this stream that match the given predicate.

 

주어진 Predicate에 일치하는 요소만 구성된 스트림을 반환한다라고 되어 있습니다.
그래서 짝수만 필터링은 .filter(n -> n % 2 == 0) 를 써주면 해당 조건에 맞는 스트림이 만들어집니다.

 

reduce 문서를 보면

T reduce(T identity, BinaryOperator<T> accumulator)

Performs a reduction on the elements of this stream, using the provided identity value and an associative accumulation function, and returns the reduced value.
but is not constrained to execute sequentially.
The identity value must be an identity for the accumulator function.
This means that for all t, accumulator.apply(identity, t) is equal to t.
The accumulator function must be an associative function.
This is a terminal operation.

Performs a reduction on the elements of this stream,

using the provided identity value and an associative accumulation function, and returns the reduced value.

 

제공된 초기값을 기준으로 누적 함수를 사용하여 요소를 축소 작업 해준다. 이게 기본적인 reduce인데

메소드 레퍼런스로  가볍게 만들어 주었다.

 

3. Stream 문제 풀어보기

백기선님이 내주신 Stream 문제를 풀어보려고 한다.

문제는 아래와 같다.

@Test
public void streamApiTest() {
    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>> keesunEvents = new ArrayList<>();
    keesunEvents.add(springClasses);
    keesunEvents.add(javaClasses);

    System.out.println("spring 으로 시작하는 수업");

    System.out.println("close 되지 않은 수업");

    System.out.println("수업 이름만 모아서 스트림 만들기");

    System.out.println("두 수업 목록에 들어있는 모든 수업 아이디 출력");

    System.out.println("10부터 1씩 증가하는 무제한 스트림 중에서 앞에 10개 빼고 최대 10개 까지만");

    System.out.println("자바 수업 중에 Test가 들어있는 수업이 있는지 확인");

    System.out.println("스프링 수업 중에 제목에 spring이 들어간 것만 모아서 List로 만들기");
}

문제 풀이는 따로 적어두지는 않겠습니다.

 

이번 시간에는 Stream에 대해서 알아보았다.

코드 짜면서 생각보다 Stream을 쓸 때가 많아서 한번 쯤 공부 해보아야겠다고 많이 생각했었는데

이번 기회에 아주 제대로 스터디 한 것 같아 좋습니다. 감사합니다.

 

출처 : https://www.inflearn.com/course/the-java-java8/dashboard

 

더 자바, Java 8 - 인프런 | 강의

자바 8에 추가된 기능들은 자바가 제공하는 API는 물론이고 스프링 같은 제 3의 라이브러리 및 프레임워크에서도 널리 사용되고 있습니다. 이 시대의 자바 개발자라면 반드시 알아야 합니다. 이

www.inflearn.com