JAVA

Stream 스트림

Lahezy 2023. 3. 19.
728x90

배경

이전까지는 많은 양의 데이터 처리를 위해 배열이나 컬렉션을 이용해서 처리하며 반복문을 사용해서 접근해야 했다. 해당 방법은 코드가 길어지고 가독성이 떨어진다. 이 문제점을 해결하기 위해 스트림 API를 도입했다.

즉, 스트림은 자바 컬렉션과 배열 등의 데이터를 다룰때 유용한 기능으로 기존 데이터를 변경하지 않고 다양한 연산을 하여 결과를 새로운 스트림으로 반환한다.

 

장점

스트림은 데이터 소스를 추상화 하고, 데이터를 다루는데 자주 사용되는 메서드들을 정의해 둔 것이다.

이를 활용하여 코드의 재사용성을 높이고 간결하고 이해하기 쉽게한다. 또한, 병렬 처리를 쉽게 할 수 있다.

 

특징

  • 데이터 소스를 변경하지 않고 한번 생성되면 소모하면서 내부에서 반복하기 때문에 다시 재사용이 불가능하다.
  • 다양한 연산으로 복잡한 쿼리를 간단히 처리할 수 있다
  • 중간 연산과 최종연산으로 나누어 진다.
  • 중간 연산은 계속해서 연결하여 여러 개의 중간 연산을 수행할 수 있지최종 연산을 스트림의 요소를 소모하면서 연산을 수행해서 단 한 번만 연산이 가능하다.
  • 스트림은 최종 연산이 실행되기 전까지는 중간 연산까지의 과정이 수행되지 않고 최종 연산을 하여야 결과가 수행된다

스트림 선언 

다음과 같이 선언할 수 있다.

//intStream
int[] arr = {1, 2, 3, 4, 5};
// 배열 출력
IntStream stream = Arrays.stream(arr); //IntStream
stream.forEach(System.out::print); //12345
// 범위 출력 (숫자를 생성한다)
IntStream.range(0, 5).forEach(System.out::print); //01234

// 종료값 범위를 포함한 곳까지 숫자를 생성한다.
IntStream.rangeClosed(0, 5).forEach(System.out::print); //012345
// 합계 계산
int sum = Arrays.stream(arr).sum(); // 15
// 최댓값 계산
int max = Arrays.stream(arr).max().orElse(0); // 5
// 최솟값 계산
int min = Arrays.stream(arr).min().orElse(0); // 1

System.out.println();

//랜덤 숫자 만들기
IntStream intStream = new Random().ints();
intStream.limit(5).forEach(System.out::println); //Int형 범위 내에서 랜덤숫자가 만들어 진다.

 

중간 연산의 핵심 부분은 map, flatMap, 최종 연산의 핵심 부분은 reduce()와 collect()이다.

중간 연산 예시

  1. filter(): 스트림에서 주어진 조건에 맞는 요소만을 걸러낸다
  2. map(): 스트림의 요소를 변환한다.
  3. flatMap(): 스트림의 요소를 1:1 또는 1:n으로 변환한다.
  4. distinct(): 스트림에서 중복된 요소를 제거한다.
  5. sorted(): 스트림을 정렬한다.
  6. peek(): 요소를 소비하면서 중간 결과를 출력한다.
  7. limit(): 스트림에서 처음 개의 요소만을 추출한다.
  8. skip(): 스트림에서 처음 개의 요소를 건너뛰고 시작한다.
  9. takeWhile(): 스트림에서 주어진 조건을 만족하는 요소만 추출한다.
List<String> strings = Arrays.asList("apple", "banana", "cherry", "durian","orange","grape");

// filter(): 문자열의 길이가 5 이하인 요소만 걸러내는 예시
List<String> filtered = strings.stream().filter(s -> s.length() <= 5).collect(Collectors.toList());
System.out.println(filtered); // [apple, grape]

// map(): 문자열을 대문자로 변환하는 예시
List<String> mapped = strings.stream().map(s -> s.toUpperCase()).collect(Collectors.toList());
System.out.println(mapped); //[APPLE, BANANA, CHERRY, DURIAN, ORANGE, GRAPE]

// sorted(): 문자열을 길이에 따라 오름차순으로 정렬하는 예시
List<String> sorted = strings.stream().sorted(Comparator.comparing(String::length)).collect(Collectors.toList());
System.out.println(sorted); // [apple, grape, banana, cherry, durian, orange]

//limit(): 스트림의 요소중 처음 n개만 추출하는 예시
List<String> limit = strings.stream().limit(3).collect(Collectors.toList());
System.out.println(limit); // [apple, banana, cherry]
List<Integer> list = Arrays.asList(1, 2, 3, 1, 2, 3);

//distinct(): 중복된 요소를 제거하는 예시
Stream<Integer> stream = list.stream().distinct(); //중간 연산에서 반환시
System.out.println(stream.collect(Collectors.toList())); // [1, 2, 3]

최종연산예시

요소를 소비하면서 진행한다, 한 번만 가능

  1. forEach() : 요소에 대해 주어진 작업을 수행한다.
  2. count() : 스트림에 포함된 요소의 수를 반환한다.
  3. reduce() : 스트림의 모든 요소를 사용하여 하나의 결과값을 계산한다.
  4. collect(): 스트림의 요소를 수집하여 리스트, , 등의 컬렉션으로 반환하는 한다.
List<String> strings = Arrays.asList("apple", "banana", "cherry", "durian", "orange", "grape");
//forEach()
strings.stream().forEach(System.out::println);//위의 모든 문자열 한번씩 출력 

//count()
long count = strings.stream().count();
System.out.println(count); // 6

//reduce()
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
System.out.println(sum); //15

//filter()
List<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());
System.out.println(evenNumbers); // [2, 4]

사용 예시 

학생의 점수를 10점 단위로 나눠주는 예시

public static void main(String[] args) {
    // 학생들의 점수가 담긴 리스트
    List<Integer> scores = IntStream.rangeClosed(0, 100).boxed().collect(Collectors.toList());

    // 점수를 10점 단위로 그룹화하고, 그룹별로 학생 수 계산하기
    Map<Object, Long> scoreDistribution = scores.stream()
            .collect(Collectors.groupingBy(score -> score / 10 * 10, Collectors.counting()));

    // 결과 출력하기
    System.out.println("학생수 = " + scores.stream().count());
    for (int i = 0; i < 100; i += 10) {
        System.out.printf("%d~%d점: %d명\n", i, i + 10, scoreDistribution.getOrDefault(i, 0L));
    }
    System.out.printf("100점 : %d명\n", scoreDistribution.getOrDefault(100, 0L));
    /**
     * output:
     * 학생수 = 101
     * 0~10점: 10명
     * 10~20점: 10명
     * 20~30점: 10명
     * 30~40점: 10명
     * 40~50점: 10명
     * 50~60점: 10명
     * 60~70점: 10명
     * 70~80점: 10명
     * 80~90점: 10명
     * 90~100점: 10명
     * 100점 : 1명
     */
}

 

[참고]

자바의 정석 3판

https://futurecreator.github.io/2018/08/26/java-8-streams/

https://gem1n1.tistory.com/159

728x90

'JAVA' 카테고리의 다른 글

JVM의 동작 방식과 메모리 구조  (0) 2023.04.26
람다식(Lambda Expression)  (1) 2023.03.08
Enum 클래스  (0) 2023.02.28
ArrayList와 LinkedList  (0) 2023.02.24
오버로딩과 오버라이딩  (0) 2023.02.22

댓글