웹(WEB)/자바(Java)

[Java] 자바 Stream API

마달랭 2024. 12. 31. 02:36
반응형

개요

자바 Stream API는 자바 8에서 도입된 기능으로, 컬렉션 데이터에 대해 함수형 프로그래밍 스타일로 데이터를 처리할 수 있게 해준다.

Stream은 데이터의 흐름을 의미하며, 컬렉션이나 배열에 저장된 데이터를 일련의 연산을 통해 처리할 수 있도록 돕는다.

 

Stream API는 컬렉션이나 배열과 같은 데이터 소스를 처리할 수 있으며, 주로 배열, 컬렉션, 입출력 스트림 등을 사용하여 선언적 방식으로 데이터 처리 로직을 작성할 수 있다.

주요 특징은 람다식과 함께 사용되어 가독성을 높이고, 병렬 처리를 손쉽게 할 수 있다는 점이다.

 

Stream API는 컬렉션의 데이터를 쉽게 변환하고 처리하는 데 매우 유용하다.

예를 들어, 리스트의 중복을 제거하거나, 객체의 특정 필드만 뽑아서 새로운 리스트로 만들거나, 조건에 맞는 값만 뽑아내는 등의 작업을 선언적으로 처리할 수 있다.

 

 

Stream의 기본 특징

 

  • 선언적: 코드의 의도를 명확히 하며, 선언적인 방식으로 데이터를 처리한다.
  • 불변성: Stream은 원본 데이터를 변경하지 않고 변환된 결과를 새 Stream으로 반환한다.
  • 지연 평가(Lazy Evaluation): Stream의 연산은 결과가 실제로 필요할 때까지 실행되지 않는다.
  • 파이프라인(Pipeline): 연산은 중간 연산과 최종 연산으로 나뉘며, 파이프라인을 통해 연속적으로 처리된다.

 

 

중간 연산 메서드

중간 연산은 Stream을 변환하거나 필터링하는 연산으로, 여러 중간 연산을 파이프라인으로 연결할 수 있다.

중간 연산은 지연 평가되므로 최종 연산이 호출될 때까지 실행되지 않는다.

 

  • filter(): 조건에 맞는 요소를 필터링한다.
  • map(): 각 요소를 변환한다.
  • flatMap(): 각 요소를 다른 컬렉션으로 펼친다.
  • distinct(): 중복을 제거한다.
  • sorted(): 요소를 정렬한다.
  • peek(): Stream을 소비하지 않고, 각 요소를 출력할 수 있다.

 

 

최종 연산 메서드

최종 연산은 Stream을 소비하여 결과를 반환하는 연산이다.

최종 연산이 호출되면 Stream은 더 이상 사용할 수 없다.

 

  • forEach(): 각 요소에 대해 작업을 수행한다.
  • collect(): 요소들을 수집하여 다른 형태로 변환한다.
  • reduce(): 요소들을 하나로 합친다.
  • count(): 요소의 개수를 반환한다.
  • min() / max(): 최소값과 최대값을 반환한다.
  • anyMatch(), allMatch(), noneMatch(): 조건을 만족하는 요소가 있는지 확인한다.
  • findFirst(), findAny(): 첫 번째 요소나 아무거나 반환한다.

 

 

filter()

import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

        // 짝수만 필터링
        List<Integer> evenNumbers = numbers.stream()
            .filter(n -> n % 2 == 0)  // 필터링
            .collect(Collectors.toList());  // 결과를 리스트로 수집

        System.out.println(evenNumbers);  // [2, 4, 6]
    }
}
  1. Interger객체 타입 리스트 numbers에서 filter를 통해 짝수만 필터링 하였다.
  2. 이후 collect를 통해 결과를 리스트로 수집하여 evenNumbers 리스트에 파싱하였다.
  3. evenNumbers의 요소를 출력하면 짝수만 존재함을 알 수 있다.

 

map()

import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

        // 각 이름을 대문자로 변환
        List<String> upperNames = names.stream()
            .map(String::toUpperCase)  // 대문자로 변환
            .collect(Collectors.toList());  // 결과를 리스트로 수집

        System.out.println(upperNames);  // [ALICE, BOB, CHARLIE]
    }
}
  1. String객체 타입 리스트 names에서 각 요소에 map을 통해 String클래스의 함수 toUpperCase를 적용해 주었다.
  2. 이후 collect를 통해 결과를 리스트로 수집하여 upperNames 리스트에 파싱하였다.
  3. upperNames의 요소를 출력하면 모든 문자열이 대문자로 바뀐 것을 볼 수 있다.

 

reduce()

import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // 합산
        int sum = numbers.stream()
            .reduce(0, (a, b) -> a + b);  // 초기값 0부터 시작하여 각 값을 더함

        System.out.println(sum);  // 15
    }
}
  1. Integer객체 타입 리스트 numbers에서 reduce를 통해 초기값 0부터 각 요소를 더해주었다.
  2. sum을 출력할 경우 각 요소의 값 15가 출력됨을 알 수 있다.

 

복합적인 Stream 처리

import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("John", "Paul", "George", "Ringo");

        // 이름 길이가 4 이상인 이름만 대문자로 변환 후 정렬
        List<String> result = names.stream()
            .filter(name -> name.length() >= 4)
            .map(String::toUpperCase)
            .sorted()
            .collect(Collectors.toList());

        System.out.println(result);  // [GEORGE, RINGO]
    }
}
  1. String객체 타입 리스트 names를 초기화 한다.
  2. filter를 통해 이름의 길이가 5이상인 요소만 필터링 한다.
  3. map을 통해 해당 문자열들을 String클래스의 toUpperCase메서드를 활용해 대문자로 변경해 준다.
  4. sorted를 통해 각 요소들을 오름차순으로 정렬해 준다.
  5. collect를 통해 리스트로 변경 후 String객체 타입의 리스트 result에 저장해 준다.
  6. result를 출력 시 이름이 길이가 5이상인 이름이 대문자로 출력됨을 알 수 있다.

 

병렬 스트림

Stream API는 병렬 처리를 쉽게 할 수 있도록 도와준다.

parallelStream()을 사용하면 데이터가 자동으로 여러 스레드에서 처리된다.

병렬 스트림은 멀티코어 CPU에서 성능을 극대화할 수 있다.

import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

        // 병렬 스트림으로 처리
        int sum = numbers.parallelStream()
            .reduce(0, Integer::sum);  // 합산

        System.out.println(sum);  // 21
    }
}

 

 

분할 (Partitioning)

분할은 데이터를 두 그룹으로 나누는 작업으로, 주로 boolean 조건을 사용한다.

Collectors.partitioningBy()는 주어진 조건을 만족하는 요소를 하나의 그룹으로, 그렇지 않은 요소를 다른 그룹으로 나누어 Map<Boolean, List<T>> 형태로 반환한다.

import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // 짝수와 홀수로 분할
        Map<Boolean, List<Integer>> partitioned = numbers.stream()
            .collect(Collectors.partitioningBy(n -> n % 2 == 0));

        System.out.println(partitioned);  // {false=[1, 3, 5, 7, 9], true=[2, 4, 6, 8, 10]}
    }
}

 

  • partitioningBy()는 true와 false의 두 그룹으로 나누어 Map<Boolean, List<T>> 형태로 결과를 반환한다.
  • 위 예제에서는 짝수를 true 그룹에, 홀수를 false 그룹에 배치한다.

 

 

그루핑 (Grouping)

그루핑은 데이터를 특정 기준에 따라 그룹화하는 작업이다.

Collectors.groupingBy()는 조건에 맞는 그룹을 Map 키-값 쌍으로 만들며, 반환형은 Map<K, List<T>>이다.

그룹화 기준은 주로 Function<T, K>를 사용해 정할 수 있다.

import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("apple", "banana", "avocado", "blueberry", "cherry", "Bam");

        // 첫 글자로 그룹화
        Map<Character, List<String>> grouped = words.stream()
                .collect(Collectors.groupingBy(word -> word.charAt(0)));

        System.out.println(grouped);  // {a=[apple, avocado], B=[Bam], b=[banana, blueberry], c=[cherry]}
    }
}

 

  • groupingBy()는 각 요소를 그룹화 기준에 따라 나누고, Map<K, List<T>> 형식으로 반환한다.
  • 위 예제에서는 단어의 첫 글자를 기준으로 그룹화했다.
  • charAt메서드는 대소문자를 구분한다.

 

728x90
반응형

'웹(WEB) > 자바(Java)' 카테고리의 다른 글

[Java] 자바 람다 Lambda  (0) 2024.12.31
[Java] 자바 자료구조, Collection  (0) 2024.12.31
[Java] 자바 annotation  (0) 2024.12.31
[Java] 자바 Enum  (0) 2024.12.31
[Java] 자바 Generics  (0) 2024.12.30