JAVA - Stream

최정환·2023년 4월 2일
0

Practice JAVA

목록 보기
10/10

스트림(Stream)은 병렬 또는 순차적으로 처리할 수 있는 요소 시퀀스입니다.

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

⭐️ 스트림의 특징

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

💡 스트림 vs 일반 메서드

스트림의 map과 컬렉션의 유사 메서드는 중요한 차이점이 있습니다.

  • stream.map: 스트림의 중간 연산으로 스트림을 반환하며 지연되어 수행됩니다.
  • 컬렉션의 메서드: 예를 들어 replaceAll은 컬렉션을 반환하고 즉시 수행됩니다.
List<String> list = Arrays.asList("a", "b", "c");

// 스트림의 map
list.stream()
    .map(String::toUpperCase)
    .forEach(System.out::println);

// 컬렉션의 replaceAll
list.replaceAll(String::toUpperCase);
System.out.println(list);

1️⃣ 스트림 생성 방법

  1. 컬렉션에서 생성

    List<String> list = Arrays.asList("a", "b", "c");
    Stream<String> stream = list.stream();
    stream.forEach(System.out::println);
  2. 배열에서 생성

    int[] arr = {1, 2, 3, 4, 5};
    IntStream intStream = Arrays.stream(arr);
    intStream.forEach(System.out::println);
  3. 빌더 사용

    Stream<String> builderStream = Stream.<String>builder()
        .add("Apple")
        .add("Banana")
        .add("Melon")
        .build();
    builderStream.forEach(System.out::println);
  4. generate() 메소드

    Stream<String> generateStream = Stream.generate(() -> "Hello")
        .limit(5);
    generateStream.forEach(System.out::println);
  5. iterate() 메소드

    Stream<Integer> iterateStream = Stream.iterate(100, n -> n + 10)
        .limit(5);
    iterateStream.forEach(System.out::println);
  6. empty() 메소드

    Stream<String> emptyStream = Stream.empty();
    System.out.println(emptyStream.count()); // 0
  7. 기본 타입 스트림

    IntStream intStream = IntStream.range(1, 10);
    intStream.forEach(System.out::println);

2️⃣ 스트림 연산

  1. filter

    List<String> names = Arrays.asList("Jun", "James", "Chris", "Uni");
    names.stream()
        .filter(name -> name.startsWith("J"))
        .forEach(System.out::println); // Jun, James
  2. map

    List<String> fruits = Arrays.asList("apple", "banana", "melon", "grape");
    fruits.stream()
        .map(String::toUpperCase)
        .forEach(System.out::println); // APPLE, BANANA, MELON, GRAPE
  3. sorted

    List<Integer> numbers = Arrays.asList(5, 3, 1, 4, 2);
    numbers.stream()
        .sorted()
        .forEach(System.out::println); // 1, 2, 3, 4, 5
  4. 집계 연산 (count, sum, average, min, max)

    int[] scores = {80, 90, 100, 70, 85};
    IntSummaryStatistics stats = Arrays.stream(scores).summaryStatistics();
    System.out.println("개수: " + stats.getCount());
    System.out.println("합계: " + stats.getSum());
    System.out.println("평균: " + stats.getAverage());
    System.out.println("최대: " + stats.getMax());
    System.out.println("최소: " + stats.getMin());
  5. 매칭 연산 (anyMatch, allMatch, noneMatch)

    List<String> colors = Arrays.asList("red", "blue", "green", "yellow");
    boolean result1 = colors.stream().anyMatch(color -> color.equals("red")); // true
    boolean result2 = colors.stream().allMatch(color -> color.length() == 4); // false
    boolean result3 = colors.stream().noneMatch(color -> color.startsWith("p")); // true
    System.out.println(result1); // true
    System.out.println(result2); // false
    System.out.println(result3); // true
  6. reduce

    List<String> animals = Arrays.asList("dog", "cat", "bird", "fish");
    String result4 = animals.stream()
        .reduce("", (a, b) -> a + b);
    System.out.println(result4); // dogcatbirdfish
  7. collect

    List<String> fruits = Arrays.asList("apple", "banana", "melon", "grape");
    List<String> result5 = fruits.stream()
        .filter(fruit -> fruit.length() == 5)
        .collect(Collectors.toList());
    result5.forEach(System.out::println); // apple, grape

⭐️ 병렬 처리 (Parallel Processing)

병렬 처리는 대용량 데이터 처리에 유용하며 스트림 API에서도 지원됩니다. parallel() 메소드를 호출하면 ForkJoin 프레임워크를 사용하여 여러 스레드가 작업을 수행합니다.

import java.util.Arrays;

public class ParallelStreamExample {
    public static void main(String[] args) {
        int[] numbers = {1,2,3,4,5,6,7,8,9,10};

        long start = System.currentTimeMillis();

        long count = Arrays.stream(numbers)
                .parallel()
                .filter(n -> n % 2 == 0)
                .count();

        long end = System.currentTimeMillis();
        System.out.println("짝수의 개수: " + count);
        System.out.println("걸린 시간(ms): " + (end - start));
    }
}

장점

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

단점

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

정리

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

0개의 댓글