[Java] Stream API

2025. 4. 6. 10:00·Back/02. Java
반응형

Stream API 란? 

Java에서 데이터를 저장하기 위해 배열이나 Collection을 사용하는데, 저장된 데이터에 접근하기 위해서 반복문이나 반복자를 사용하여 코드를 작성하는데, 이렇게 작성한 코드는 길고, 가독성이 떨어지며 재사용성도 거의 불가능하다. 

이를 해결하기 위해 Java SE 8부터 Stream API를 도입했다.

Stream API는 데이터를 추상화하여 다루어 저장된 데이터를 여러 방식으로 공통된 방법을 제공한다. 

Stream 

데이터 흐름(연속적 요소)를 의미

병렬 처리를 지원해 성능 최적화가 가능하다. 

Stream API 특징 

내부 반복(internal iteration)을 통해 작업을 수행

재사용이 가능한 컬렉션과는 달리 단 한 번만 사용 가능

원본 데이터 변경 X

지연 연산을 사용해 지연(lazy) 연산을 통해 성능 최적화

parallelStream() 메소드 → 병렬 처리 지원

Stream API 기본 구조 

1️⃣ 데이터 소스(Collection, 배열, 파일 등)에서 스트림 생성
2️⃣ 중간 연산(Intermediate Operation)으로 데이터 변환 및 가공
3️⃣ 최종 연산(Terminal Operation)으로 결과 생성

Stream 동작 방식

1. 스트림의 생성
2. 스트림의 중개 연산 (스트림의 변환)
3. 스트림의 최종 연산 (스트림의 사용)

Stream API 동작 흐름 (출처 : TCPSchool)

Stream 중개 연산 (intermediate operation)

Stream API에 의해 초기 Stream은 중개 연산 통해 또 다른 Stream으로 변환

중개 연산은 연속으로 연결해서 사용 가능 

filter-map 기반의 API 사용하여 지연(lazy) 연산을 통해 최적화 가능 

결과를 즉시 반환하지 않고, 최종 연산이 실행될 때까지 대기

Stream 필터링

filter : 조건에 맞는 요소로 새로운 스트림 반환 

distinct : 중복된 요소가 제거된 새로운 Stream 반환, 내부적으로 Object 클래스의 equals 메서드 사용해 중복 비교

Stream 변환

map : 주어진 함수에 인수로 전달해 반환값들로 이루어진 새로운 Stream 반환

flatMap : 해당 Stream 요소가 배열일 때, 각 배열의 각 요소의 반환값을 하나로 합친 새로운 Stream 얻을 수 있음 

Stream 제한 

limit : 첫 번째 요소부터 전달된 개수만큼의 요소만으로 이루어진 새로운 스트림 반환

skip : 첫 번째 요소부터 전달된 개수만큼의 요소를 제외한 나머지 요소만으로 이루어진 새로운 스트림 반환

Stream 정렬 

sorted : 비교자(comparator)를 이용하여 정렬, 비교자가 없을 때, 기본적으로 사전 편찬 순(natural order)으로 정렬

Stream 연산 결과 확인 

peek : 결과 스트림으로부터 요소를 소모하여 추가로 명시된 동작 수행, 원본 스트림에서 요소를 소모X, 연산과 연산 사이에 결과를 확인할 때 사용, 디버깅 용도로 주로 사용됨 

메소드 연산
Stream<T> filter(Predicate<? super T> predicate) 해당 스트림에서 주어진 조건(predicate)에 맞는 요소만으로 구성된 새로운 스트림을 반환함.
<R> Stream<R> map(Functoin<? super T, ? extends R> mapper) 해당 스트림의 요소들을 주어진 함수에 인수로 전달하여, 그 반환값으로 이루어진 새로운 스트림을 반환함.
<R> Stream<R> flatMap(Functoin<? super T, ? extends Stream<? extends R>> mapper) 해당 스트림의 요소가 배열일 경우, 배열의 각 요소를 주어진 함수에 인수로 전달하여, 그 반환값으로 이루어진 새로운 스트림을 반환함.
Stream<T> distinct() 해당 스트림에서 중복된 요소가 제거된 새로운 스트림을 반환함.
내부적으로 Object 클래스의 equals() 메소드를 사용함.
Stream<T> limit(long maxSize) 해당 스트림에서 전달된 개수만큼의 요소만으로 이루어진 새로운 스트림을 반환함.
Stream<T> peek(Consumer<? super T> action) 결과 스트림으로부터 각 요소를 소모하여 추가로 명시된 동작(action)을 수행하여 새로운 스트림을 생성하여 반환함.
Stream<T> skip(long n) 해당 스트림의 첫 번째 요소부터 전달된 개수만큼의 요소를 제외한 나머지 요소만으로 이루어진 새로운 스트림을 반환함.
Stream<T> sorted()
Stream<T> sorted(Comparator<? super T> comparator)
해당 스트림을 주어진 비교자(comparator)를 이용하여 정렬함.
비교자를 전달하지 않으면 영문사전 순(natural order)으로 정렬함.

Stream 최종 연산

최종 연산이 호출되면 Stream은 종료됨 (1회만 사용 가능)

지연(lazy)되었던 모든 중개 연산들이 최종 연산 시에 모두 수행

최종 연산 시에 모든 요소를 소모한 해당 스트림은 더는 사용할 수 없게 된다. 

요소의 출력

forEach : 스트림의 각 요소를 소모하여 명시된 동작 수행, 스트림의 모든 요소를 출력하는 용도로 주로 사용

요소의 소모

reduce : 첫 번째와 두 번째 요소를 가지고 연산을 수행한 뒤, 그 결과와 세 번째 요소를 가지고 또다시 연산을 수행하여 모든 요소를 소모하여 연산을 수행해 결과 반환. 

요소의 검색

findFirst

findAny

요소의 검사

anyMatch : 해당 스트림의 일부 요소가 특정 조건을 만족할 경우에 true를 반환

allMatch : 해당 스트림의 모든 요소가 특정 조건을 만족할 경우에 true를 반환 

noneMatch : 해당 스트림의 모든 요소가 특정 조건을 만족하지 않을 경우에 true를 반환

요소의 통계

count :  해당 스트림의 요소의 총 개수를 long 타입의 값 

min : 스트림의 요소 중에서 가장 작은 값을 가지는 요소를 참조하는 Optional 객체를 얻을 수 있다.

max : 스트림의 요소 중에서 가장 큰 값을 가지는 요소를 참조하는 Optional 객체를 얻을 수 있다.

요소의 연산

sum : 해당 스트림의 모든 요소에 대해 합

average : 해당 스트림의 모든 요소에 대해 평균, Optional 객체를 반환

요소의 수집

collect :

Collectors 객체에 구현된 방법대로 스트림의 요소를 수집

Collectors 클래스에는 미리 정의된 다양한 방법이 클래스 메소드로 정의

그 외

1. 스트림을 배열이나 컬렉션으로 변환 : toArray(), toCollection(), toList(), toSet(), toMap()
2. 요소의 통계와 연산 메소드와 같은 동작을 수행 : counting(), maxBy(), minBy(), summingInt(), averagingInt() 등
3. 요소의 소모와 같은 동작을 수행 : reducing(), joining()
4. 요소의 그룹화와 분할 : groupingBy(), partitioningBy()

 

메소드 설명
void forEach(Consumer<? super T> action) 스트림의 각 요소에 대해 해당 요소를 소모하여 명시된 동작을 수행함.
Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
처음 두 요소를 가지고 연산을 수행한 뒤, 그 결과와 다음 요소를 가지고 또다시 연산을 수행함. 이런 식으로 해당 스트림의 모든 요소를 소모하여 연산을 수행하고, 그 결과를 반환함.
Optional<T> findFirst()
Optional<T> findAny()
해당 스트림에서 첫 번째 요소를 참조하는 Optional 객체를 반환함.
(findAny() 메소드는 병렬 스트림일 때 사용함)
boolean anyMatch(Predicate<? super T> predicate) 해당 스트림의 일부 요소가 특정 조건을 만족할 경우에 true를 반환함.
boolean allMatch(Predicate<? super T> predicate) 해당 스트림의 모든 요소가 특정 조건을 만족할 경우에 true를 반환함.
boolean noneMatch(Predicate<? super T> predicate) 해당 스트림의 모든 요소가 특정 조건을 만족하지 않을 경우에 true를 반환함.
long count() 해당 스트림의 요소의 개수를 반환함.
Optional<T> max(Comparator<? super T> comparator) 해당 스트림의 요소 중에서 가장 큰 값을 가지는 요소를 참조하는 Optional 객체를 반환함.
Optional<T> min(Comparator<? super T> comparator) 해당 스트림의 요소 중에서 가장 작은 값을 가지는 요소를 참조하는 Optional 객체를 반환함.
T sum() 해당 스트림의 모든 요소에 대해 합을 구하여 반환함.
Optional<T> average() 해당 스트림의 모든 요소에 대해 평균값을 구하여 반환함.
<R,A> R collect(Collector<? super T,A,R> collector) 인수로 전달되는 Collectors 객체에 구현된 방법대로 스트림의 요소를 수집함.

병렬 Stream

Parallel Processing 병렬 처리를 지원해 대용량 데이터를 빠르게 처리 가능하다.

멀티코어 CPU를 활용해 성능이 향상되지만, 정렬이 필요한 경우, 병렬 처리는 결과가 예상 결과와 다를 수 있다. 

 

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

class Employee {

    private String name;
    private int salary;
    private String department;

    public Employee(String name, int salary, String department) {
    
        this.name = name;
        this.salary = salary;
        this.department = department;
    }

    public String getName() {
        return name;
    }

    public int getSalary() {
        return salary;
    }

    public String getDepartment() {
        return department;
    }

    @Override
    public String toString() {
        return name + " - " + salary + " (" + department + ")";
    }
}

public class StreamMixedExample {
    public static void main(String[] args) {

        // 직원 목록 생성
        List<Employee> employees = Arrays.asList(
                new Employee("Alice", 6000, "HR"),
                new Employee("Bob", 4000, "IT"),
                new Employee("Charlie", 5500, "Finance"),
                new Employee("David", 7000, "IT"),
                new Employee("Eve", 4800, "HR")
        );

        // Stream API 혼합 사용
        // filter : 필터링
        // map : 변환
        // sorted : 정렬
        // parallelStream : 병렬
        // toList : 리스트 변환 
        List<String> highSalaryNames = employees.parallelStream() // 병렬 처리
                .filter(emp -> emp.getSalary() >= 5000)  // 급여 5000 이상 필터링
                .map(emp -> emp.getName().toUpperCase()) // 이름을 대문자로 변환
                .sorted() // 이름순 정렬
                .collect(Collectors.toList()); // 리스트로 변환

        // 결과 출력
        System.out.println("급여 5000 이상 직원 이름 목록: " + highSalaryNames);

        // 급여 총합 계산 (reduce 사용)
        // map 
        // reduce
        int totalSalary = employees.stream()
                .map(Employee::getSalary)
                .reduce(0, Integer::sum);

        System.out.println("총 급여 합계: " + totalSalary);

        // 부서별 그룹화
        // groupingBy
        Map<String, List<Employee>> departmentGroup = employees.stream()
                .collect(Collectors.groupingBy(Employee::getDepartment));

        System.out.println("부서별 직원 목록:");
        // forEach
        departmentGroup.forEach((dept, empList) -> {
            System.out.println(dept + " -> " + empList);
        });
    }
}
반응형

'Back > 02. Java' 카테고리의 다른 글

[Java] Java Collection Framework, JCF  (0) 2025.04.05
[Java] Java의 Exception 예외 처리  (2) 2025.04.01
[Java] 객체 지향 프로그래밍과 필수 개념들  (1) 2025.03.31
[Java] JDK, JRE, JVM 정리하기  (0) 2025.03.30
[Java] JPA 입문  (0) 2025.03.25
'Back/02. Java' 카테고리의 다른 글
  • [Java] Java Collection Framework, JCF
  • [Java] Java의 Exception 예외 처리
  • [Java] 객체 지향 프로그래밍과 필수 개념들
  • [Java] JDK, JRE, JVM 정리하기
물떡망대
물떡망대
  • 물떡망대
    망망대해 개발 기록
    물떡망대
  • 전체
    오늘
    어제
    • 분류 전체보기 (29)
      • Front (2)
        • 01. JavaScript & TypeScript (1)
        • 02. React (1)
        • 03. Html & Jsp (0)
        • 04. CSS (0)
      • Back (18)
        • 01. Spring (5)
        • 02. Java (6)
        • 03. SQL (3)
        • 04. API 설계 (4)
        • 05. MSA & 아키텍처 (0)
      • SERVER & Infra (6)
        • 01. Linux & Ngnix (3)
        • 02. Docker & Kubernetes (2)
      • DEV & Collaboration (2)
        • 01. Git & GitHub (2)
        • 02. 코딩 컨벤션 (0)
      • 취미공간 (0)
        • 독서 공간 (0)
        • 그림 및 휴식공간 (0)
        • 여행일지 (0)
      • Study (1)
        • 자격증 (0)
        • CS 지식 (0)
        • 코딩테스트 (0)
        • 프로젝트 (0)
        • 개인 포트폴리오 (0)
  • 블로그 메뉴

    • 홈
  • 링크

    • GitHub
    • Naver Blog
  • 공지사항

  • 인기 글

  • 태그

    SQL 권한
    SQL
    백엔드 개발자
    spring
    tibero
    쿠버네티스 도커
    리눅스
    ORM
    JPQL
    Linux
    권한
    백엔드
    서버 개발자
    개발자
    풀스택 개발자
    HTTP
    풀스택
    오버 플로우
    Java
    교차출처요청
    Backend
    restless api
    서버
    암호화 계층
    API
    docker
    프론트엔드
    쿠버네티스 도커 차이
    JPA
    api 요청
  • 최근 댓글

  • 최근 글

  • 반응형
  • hELLO· Designed By정상우.v4.10.3
물떡망대
[Java] Stream API
상단으로

티스토리툴바