JAVA

[JAVA] 효율적인 코딩을 도와주는 Stream API

SoU330 2024. 10. 31. 07:27

 

 

Stream이란?

컬렉션 데이터를 다룰 때 효율적이고 직관적인 방법을 제공하는 API

다양한 데이터 소스를 표준화된 방법으로 다루기 위한 것

Stream을 통해 데이터의 필터링, 정렬, 변환, 집계 등을 함수형 프로그래밍 스타일로 구현할 수 있다.

 

 

 

 

특징

  • 단일 사용 : Stream은 한 번만 사용 가능한 단일 데이터 흐림이다. 한 번 사용한 Stream은 재사용 할 수 없으며 새로운 Stream을 생성해야 한다.
  • 내부 반복 : 기존의 외부 반복(for-each)을 대체하여 내부적으로 데이터 처리를 반복하는 방식이다.
  • 지연 연산 : Stream에서 filter(), map() 같은 중간 연산은 즉시 실행되지 않고 최종 연산이 호출될 때 실행된다. 불필요한 연산을 줄일 수 있다.
  • 병렬 처리 기능 : parallelStream() 을 통해 쉽게 병렬 처리를 할 수 있어 대용량 데이터 처리 시 성능 향상을 기대할 수 있다.
  • Read-Only : Stream은 원본 데이터를 변경하지 않는다.

 

 

 

 

 

생성 -> 중간 연산(n번) -> 최종 연산(1번)

 

 

 

Stream 생성

주로 컬렉션, 배열, 범위 등의 데이터 소스에서 생성된다.

// 컬렉션에서 생성
List<String> list = Arrays.asList("apple", "banana", "cherry");
Stream<String> stream = list.stream();

// 배열에서 생성
String[] arr = {"apple", "banana", "cherry"};
Stream<String> streamFromArray = Arrays.stream(arr);

// 범위에서 생성
IntStream intStream = IntStream.range(1, 10);  // 1 ~ 9
LongStream longStream = LongStream.rangeClosed(1, 10);  // 1 ~ 10

 

 

 

 

중간 연산

중간 연산은 여러 개를 연이어 사용할 수 있으며 최종 연산이 호출될 때까지 실행되지 않는 지연 연산이다.

filter(Predicate<T> predicate) : 조건에 맞는 요소만 선택한다.

stream.filter(s -> s.startsWith("a"));

 

map(Function<T, R> mapper) : 요소를 변환한다.

stream.map(String::toUpperCase);

 

flatMap(Function<T, Stream<R>> mapper) : 중첩 구조를 평평하게 만든다.

Stream<List<String>> listStream = Stream.of(
    Arrays.asList("a", "b"), Arrays.asList("c", "d"));
listStream.flatMap(Collection::stream);

 

distinct() : 중복된 요소를 제거한다.

stream.distinct();

 

sorted() : 요소를 정렬한다.

stream.sorted();

 

limit(long maxSize) : 지정한 개수만큼 요소를 선택한다.

stream.limit(3);

 

skip(long n) : 처음 n개의 요소를 건너뛴다.

stream.skip(2);

 

 

 

 

최종 연산

최종 연산은 스트림을 닫고 결과를 반환하며 최종 연산 후에는 스트림을 재사용할 수 없다.

forEach(Consumer<T> action) : 각 요소를 소비한다.

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

 

collect(Collector<T, A, R> collector) : 결과를 컬렉션으로 수집한다.

List<String> resultList = stream.collect(Collectors.toList());

 

count() : 요소의 개수를 반환한다.

long count = stream.count();

 

reduce(BinaryOperator<T> accumulator) : 모든 요소를 누적하여 하나의 결과로 만든다.

Optional<String> reduced = stream.reduce((s1, s2) -> s1 + s2);

 

allMatch(Predicate<T> predicate) : 모든 요소가 조건에 맞으면 true를 반환한다.

boolean allMatch = stream.allMatch(s -> s.length() > 1);

 

noneMatch(Predicate<T> predicate) : 조건에 맞는 요소가 하나도 없으면 true를 반환한다.

boolean noneMatch = stream.noneMatch(s -> s.startsWith("z"));

 

findFirst() : 첫 번째 요소를 반환한다.

Optional<String> firstElement = stream.findFirst();

 

findAny() : 아무 요소나 하나 반환한다.

Optional<String> anyElement = stream.findAny();

 

 

 

 

 

종합 활용 예시

List<String> words = Arrays.asList("apple", "banana", "avocado", "blueberry", "kiwi");

List<String> result = words.stream()
                           .filter(word -> word.startsWith("a"))  // 'a'로 시작하는 단어 필터
                           .map(String::toUpperCase)               // 대문자로 변환
                           .sorted()                               // 정렬
                           .collect(Collectors.toList());          // 리스트로 수집

System.out.println(result); // [APPLE, AVOCADO]