본문 바로가기

JAVA

[Java] Stream API

Stream

  • 컬렉션 데이터를 처리하기 위해 *영속된 데이터 흐름을 제공
    • (*영속 : Stream 작업 후에도 변화하지 않고 지속적으로 존재)
    • 데이터를 읽기 전용으로 처리
    • 원본 데이터를 변경시키지 않고 처리함
  • 컬렉션의 요소를 하나씩 순회하며 작업을 수행할 수 있음
  • 데이터 필터링, 집계, 변환 작업을 간단하게 표현 가능
  • 데이터 파이프라인을 제공
    • 복잡한 데이터를 간단하고 직관적으로 처리할 수 있게 해줌
  • 데이터 처리 과정 -> 함수형 프로그래밍 스타일로 작성
    • 가독성이 높아짐
    • 함수형 프로그래밍 스타일
      1. 동일한 입력에 동일한 결과물
      2. 외부 상태가 변경되지 않음
      3. 어떻게 처리할지 X -> 무엇을 처리할지 고민될 때 작성

중간 연산과 최종 연산

중간 연산

  • map(), filter(), sorted(), ...
  • 스트림을 변환하고 또 다른 스트림을 반환
  • 여러개를 연속해서 사용할 수 있음
  • 최종 연산이 호출될 때까지 실행되지 않음
  • 중간 연산은 대기하다가, 최종 연산이 호출되면 한번에 동작

최종 연산

  • forEach(), collect(), reduce(), ...
  • 스트림을 소모해서 결과를 생성
  • 최종 연산이 호출되면, 스트림은 사용할 수 없음

Stream의 장단점

장점

  • 가독성이 높아짐
  • 병렬 처리를 통한 성능 향상
    • 병렬 스트림 지원
  • 데이터 로직을 간결하게 작성해서 유지보수하기 좋음

단점

  • 경우에 따라 오버헤드를 유발할 수 있음
  • 작은 데이터셋일 경우, 기존 반복문보다 성능이 낮음
  • 디버깅이 어려움

Stream API 종류

List<String> names = Arrays.asList("Alice", "Bob", "Christine");
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
  • map() : 데이터를 변환해서 새로운 형태로 변환
// 1. map() -> 데이터를 변환해서 새로운 형태로 변환
names.stream()
        .map(String::toUpperCase)
        .forEach(System.out::println);

 

  • filter() : 조건에 맞는 요소만 필터링
// 2. filter() -> 조건에 맞는 요소만 걸러냄
names.stream()
        .filter(name -> name.length() > 3)
        .forEach(System.out::println);

 

  • reduce() : 모든 요소를 결합해서 하나의 결과를 생성
//3. reduce() -> 모든 요소를 결합해서 하나의 결과를 생성
int sum = numbers.stream()
        .reduce(0, Integer::sum);
System.out.println("Sum: " + sum);

 

  • collect() : 결과를 특정 컬렉션으로 반환
// 4. collect() -> 결과를 특정 컬렉션으로 반환
List<String> longNames = names.stream()
        .filter(name -> name.length() > 3)
        .collect(Collectors.toList());  // 결과를 리스트로 반환
System.out.println("Long names: " + longNames);

 

  • distinct() : 중복 요소 제거
// 5. distinct() -> 중복 요소 제거
List<String> duplicateNames = Arrays.asList("alice", "bob", "alace", "chris", "charles", "bob");
duplicateNames.stream()
        .distinct()
        .forEach(System.out::println);

 

  • sorted() : 정렬
// 6. sorted() -> 정렬
names.stream()
        .sorted()
        .forEach(System.out::println);

 

  • limit() : 스트림의 최대 요소 수 제한
// 7. limit() -> 스트림의 최대 요소 수 제한
numbers.stream()
        .limit(3) // 처음 3개 요소만 가져오기
        .forEach(System.out::println);

 

  • skip() : 스트림 처음 n개 요소 건너뛰기
// 8. skip() -> 스트림 처음 n개 요소 건너뛰기
numbers.stream()
        .skip(2) // 처음 2개 요소 건너뛰기
        .forEach(System.out::println);

 

  • flatMap() : 중첩 구조를 단일 구조로 펼치기
// 9. flatMap() -> 중첩 구조를 단일 구조로 펼치기
List<List<String>> nestedList = Arrays.asList(
        Arrays.asList("apple", "banana"),
        Arrays.asList("cherry", "mango")
);
nestedList.stream()
        .flatMap(Collection::stream)
        .forEach(System.out::println);

 

  • anyMatch() : 조건을 만족하는 요소가 하나라도 있는지 확인
// 10. anyMatch() -> 조건을 만족하는 요소가 하나라도 있는지 확인
boolean hasLongName = names.stream()
        .anyMatch(name -> name.length() > 5);
System.out.println("Has long name > 5: " + hasLongName);

 

  • allMatch() : 모든 요소가 조건을 만족하는지 확인
// 11. allMatch() -> 모든 요소가 조건을 만족하는지 확인
boolean allShortNames = names.stream()
        .allMatch(name -> name.length() > 5);
System.out.println("All names are shorter than 10?: " + allShortNames);

 

  • noneMatch() : 조건을 만족하는 요소가 하나도 없는지 확인
// 12. nonMatch() -> 조건을 만족하는 요소가 하나도 없는지 확인
boolean noNameStartWithZ = names.stream()
        .noneMatch(name -> name.startsWith("z")); // "z"로 시작하는 이름이 없는지 확인
System.out.println("there's no name start with 'z'?: " + noNameStartWithZ);

 

람다 표현식

  • 간결하고 읽기 쉬운 코드를 작성
  • 객체 지향 언어인 자바에서 함수를 값처럼 표현하려는 시도
  • Stream API 와 상호보완적인 관계
함수형 인터페이스
  • 함수도 객체다
  • 함수형 인터페이스는 람다 표현식을 사용할 수 있는 타입 정의
  • 자바는 객체 지향 언어이기 때문에, 람다 표현식을 단순 함수로 표현할 수는 없었음
  • 함수형 인터페이스는 자바의 타입 시스템과 람다 표현식 사이의 다리 역할
  • 함수형 인터페이스를 통해 코드 내에서 함수를 객체처럼 사용 가능
  • 익명 함수를 간단하게 표현하는 방법
    • 익명 함수 = 람다 표현식 (뉘앙스 차이가 조금 있음)
  • 메서드가 1개만 있어야 함수형 인터페이스로 사용 가능
  • 문법
    • (파라미터) -> {함수 바디}((x) -> x*x))
// Runnable
Runnable taskOle = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello Runnable");
    }
};
// Runnable 람다식
Runnable task = () -> System.out.println("Hello Runnable");
new Thread(task).start();


List<String> names = Arrays.asList("Chile", "Bob", "Ayden");
// Comparator
Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
});

// Comparator 람다식
names.sort((a, b) -> a.compareTo(b));

// Comparator 람다식 (내림차순 정렬)
names.sort((a, b) -> b.compareTo(a));

 

 

'JAVA' 카테고리의 다른 글

[Java] ConcurrentHashMap 정리  (0) 2025.03.19
[Java] Main 문  (0) 2025.02.27
[Java] 컬렉션 프레임워크  (1) 2025.01.08
[JAVA] 스트림(stream) 정리  (0) 2024.09.19
[JAVA] DAO, DTO, VO 개념  (2) 2024.05.08