2023년 4월 2일
카테고리 : JAVA
조회 : 542|1분 읽기

JAVA - Stream

스트림(Stream)은 병렬 또는 순차적으로 처리할 수 있는 요소 시퀀스입니다.
컬렉션, 배열, 파일, 난수 등 다양한 소스에서 생성할 수 있으며 map, reduce, filter, sort 등의 기능적 스타일 작업을 지원합니다. 스트림은 지연(Lazy)되어 필요할 때만 요소를 계산합니다.

⭐️ 스트림의 특징

  • 데이터 저장 없이 처리: 원본 데이터를 변경하지 않습니다.
  • 재사용 불가: 스트림 사용 후 가비지 컬렉션(GC) 대상이 됩니다.
  • 단일 처리: 데이터를 한 번에 하나씩 처리합니다.
  • 병렬 처리 지원: 여러 스레드를 활용한 병렬 처리가 가능합니다.

💡 스트림 vs 일반 메서드

스트림의 map과 컬렉션의 유사 메서드는 중요한 차이점이 있습니다.
  • stream.map: 스트림의 중간 연산으로 스트림을 반환하며 지연되어 수행됩니다.
  • 컬렉션의 메서드: 예를 들어 replaceAll은 컬렉션을 반환하고 즉시 수행됩니다.
java
1List<String> list = Arrays.asList("a", "b", "c");
2
3// 스트림의 map
4list.stream()
5    .map(String::toUpperCase)
6    .forEach(System.out::println);
7
8// 컬렉션의 replaceAll
9list.replaceAll(String::toUpperCase);
10System.out.println(list);

1️⃣ 스트림 생성 방법

  1. 컬렉션에서 생성
    java
    1List<String> list = Arrays.asList("a", "b", "c");
    2Stream<String> stream = list.stream();
    3stream.forEach(System.out::println);
  2. 배열에서 생성
    java
    1int[] arr = {1, 2, 3, 4, 5};
    2IntStream intStream = Arrays.stream(arr);
    3intStream.forEach(System.out::println);
  3. 빌더 사용
    java
    1Stream<String> builderStream = Stream.<String>builder()
    2    .add("Apple")
    3    .add("Banana")
    4    .add("Melon")
    5    .build();
    6builderStream.forEach(System.out::println);
  4. generate() 메소드
    java
    1Stream<String> generateStream = Stream.generate(() -> "Hello")
    2    .limit(5);
    3generateStream.forEach(System.out::println);
  5. iterate() 메소드
    java
    1Stream<Integer> iterateStream = Stream.iterate(100, n -> n + 10)
    2    .limit(5);
    3iterateStream.forEach(System.out::println);
  6. empty() 메소드
    java
    1Stream<String> emptyStream = Stream.empty();
    2System.out.println(emptyStream.count()); // 0
  7. 기본 타입 스트림
    java
    1IntStream intStream = IntStream.range(1, 10);
    2intStream.forEach(System.out::println);

2️⃣ 스트림 연산

  1. filter
    java
    1List<String> names = Arrays.asList("Jun", "James", "Chris", "Uni");
    2names.stream()
    3    .filter(name -> name.startsWith("J"))
    4    .forEach(System.out::println); // Jun, James
  2. map
    java
    1List<String> fruits = Arrays.asList("apple", "banana", "melon", "grape");
    2fruits.stream()
    3    .map(String::toUpperCase)
    4    .forEach(System.out::println); // APPLE, BANANA, MELON, GRAPE
  3. sorted
    java
    1List<Integer> numbers = Arrays.asList(5, 3, 1, 4, 2);
    2numbers.stream()
    3    .sorted()
    4    .forEach(System.out::println); // 1, 2, 3, 4, 5
  4. 집계 연산 (count, sum, average, min, max)
    java
    1int[] scores = {80, 90, 100, 70, 85};
    2IntSummaryStatistics stats = Arrays.stream(scores).summaryStatistics();
    3System.out.println("개수: " + stats.getCount());
    4System.out.println("합계: " + stats.getSum());
    5System.out.println("평균: " + stats.getAverage());
    6System.out.println("최대: " + stats.getMax());
    7System.out.println("최소: " + stats.getMin());
  5. 매칭 연산 (anyMatch, allMatch, noneMatch)
    java
    1List<String> colors = Arrays.asList("red", "blue", "green", "yellow");
    2boolean result1 = colors.stream().anyMatch(color -> color.equals("red")); // true
    3boolean result2 = colors.stream().allMatch(color -> color.length() == 4); // false
    4boolean result3 = colors.stream().noneMatch(color -> color.startsWith("p")); // true
    5System.out.println(result1); // true
    6System.out.println(result2); // false
    7System.out.println(result3); // true
  6. reduce
    java
    1List<String> animals = Arrays.asList("dog", "cat", "bird", "fish");
    2String result4 = animals.stream()
    3    .reduce("", (a, b) -> a + b);
    4System.out.println(result4); // dogcatbirdfish
  7. collect
    java
    1List<String> fruits = Arrays.asList("apple", "banana", "melon", "grape");
    2List<String> result5 = fruits.stream()
    3    .filter(fruit -> fruit.length() == 5)
    4    .collect(Collectors.toList());
    5result5.forEach(System.out::println); // apple, grape

⭐️ 병렬 처리 (Parallel Processing)

병렬 처리는 대용량 데이터 처리에 유용하며 스트림 API에서도 지원됩니다. parallel() 메소드를 호출하면 ForkJoin 프레임워크를 사용하여 여러 스레드가 작업을 수행합니다.
java
1import java.util.Arrays;
2
3public class ParallelStreamExample {
4    public static void main(String[] args) {
5        int[] numbers = {1,2,3,4,5,6,7,8,9,10};
6
7        long start = System.currentTimeMillis();
8
9        long count = Arrays.stream(numbers)
10                .parallel()
11                .filter(n -> n % 2 == 0)
12                .count();
13
14        long end = System.currentTimeMillis();
15        System.out.println("짝수의 개수: " + count);
16        System.out.println("걸린 시간(ms): " + (end - start));
17    }
18}

장점

  • 성능 향상: 멀티 코어 CPU를 활용하여 처리 속도를 높입니다.
  • 효율적 자원 사용: 여러 작업을 동시에 처리하여 자원 활용도를 높입니다.

단점

  • 동기화 문제: 스레드 간 데이터 공유 시 동기화가 필요합니다.
  • 오버헤드: 병렬 처리로 인한 스레드 관리 비용이 발생할 수 있습니다.
  • 처리 순서: 순서가 중요한 작업에는 적합하지 않을 수 있습니다.

정리

스트림은 데이터 처리의 효율성과 가독성을 높입니다. 병렬 처리 시 동기화 문제와 오버헤드를 고려해야 합니다. 중간 연산과 최종 연산의 차이를 이해하고 상황에 맞게 스트림을 활용하는 것이 중요합니다.