JAVA 기초

[JAVA 기초] JAVA의 정석 - Ch.14 람다와 스트림 (정리)

beginner-in-coding 2025. 1. 24. 19:23

Ch.14 람다와 스트림


-      람다식

  • 람다식이란
    • 의미: 메서드를 하나의 식(expression)으로 표현한 것(익명 함수)
    • 사용 이유: 함수를 간략하고 명확하게 표현 가능
    • 메서드와 마찬가지로 매개변수를 받을 수도 있고, 값을 반환할 수 있음
  • 람다식 작성하기
    • (매개변수 선언) -> {}
    • return 대신 식으로 대신하므로 연산 결과가 자동으로 반환됨 (';'붙이지 않음)
    • 매개변수 타입이 추론 가능하므로 생략 가능
    • 매개변수가 하나뿐인 경우(매개변수 타입이 없는 경우): 괄호() 생략 가능
    • 괄호{} 안의 문장이 하나인 경우: 생략 가능
  • 함수형 인터페이스(Functional Interface)
    • 람다식을 다루기 위한 인터페이스
    • 함수형 인터페이스에는 오직 하나의 추상 메서드만 정의되어 있어야함
    • @FunctionalInterface: 컴파일러가 함수형 인터페이스를 올바르게 작성했는지 확인함
    • 람다식을 참조변수로 다룰수 있다는 것의 의미: 메서드를 통해 람다식을 주고받을 수 있다는 의미
    • 람다식의 타입과 형변환
      • 타입은 있지만 컴파일러가 임의로 이름을 정하기 때문에 알 수 없으므로 대입 연산자의 양변의 타입을 일치시키기 위해 형변환 필수
      • 함수형 인터페이스로만 형변환 가능
  • java.util.function 패키지
    • 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 미리 정의함
    • 사용 이유:
      • 함수형 인터페이스에 정의된 메서드 이름 통일
      • 재사용성이나 유지보수에 용이
    • 자주 쓰이는 함수형 인터페이스 (매개변수 1개)
      1. Supplier<T>
        • T get(): 매개변수 없고, 반환값 존재
      2. Consumer<T>
        • void accept(T t): 매개변수 있고, 반환값 없음 (Supplier와 반대)
      3. Function<T>
        • R apply(T t): 매개변수와 반환값 존재, 일반적인 함수, 하나의 매개변수로 결과를 반환
      4. Predicate<T>
        • boolean test(T t): 조건식을 표현하는데 사용, 매개변수는 하나, 반환타입은 boolean
    • 자주 쓰이는 함수형 인터페이스 (매개변수 2개) - 접두사 Bi
      1. BiConsumer<T, U>
        • void accept(T t, U u): 두 개의 매개변수만 있고, 반환값 없음
      2. BiPredicate<T, U>
        • boolean test(T t, U u): 조건식을 표현하는데 사용, 매개변수는 둘, 반환값 존재
      3. BiFunction<T,U,R>
        • R apply(T t, U u): 두 개의 매개변수를 받아서 하나의 결과를 반환
    • UnaryOperator과 BinaryOperator: 매개변수의 타입과 반환타입의 타입이 모두 일치한다는 점을 제외하면 Function과 같음
    • 컬렉션 프레임웍과 함수형 인터페이스
      • 컬렉션 프레임웍의 인터페이스에 일부는 함수형 인터페이스를 사용
    • 기본형을 사용하는 함수형 인터페이스
      • 효율적으로 처리할 수 있도록 기본형을 사용하는 함수형 인터페이스
  • Function의 합성과 Predicate의 결합
    • Function의 합성
      • 두 람다식을 합성해서 새로운 람다식을 만드는 것
      • 순서 중요
    • Predicate의 결합
      • 여러 Predicate를 and(), or(), negate()로 연결해서 새로운 Predicate로 결합 가능
  • 메서드 참조
    • 메서드 참조(method reference): 람다식을 더 간결하게 표현하는 방법
    • 하나의 메서드만 출력하는 람다식은 '클래스이름::메서드이름' 또는 '참조변수::메서드이름'으로 표현 가능
    • 생성자의 메서드 참조: 클래스이름::new

-      스트림(stream)

  • 스트림이란
    • 데이터 소스를 추상화하고, 데이터를 다루는데 자주 사용되는 메서드들을 정의해놓음
      • 데이터 소스의 추상화 의미: 데이터 소스가 무엇이든지 같은 방식으로 다룰 수 있음(유지보수성)
    • 특징
      1. 원본 데이터 소스를 변경하지 않음
      2. 일회용임
      3. 작업을 내부 반복으로 처리
    • 스트림의 연산
      1. 중간 연산:
        • 연산 결과가 스트림
        • 스트림에 연속해서 중간연산을 할 수 있음 (n번)
      2. 최종 연산:
        • 연산 결과가 스트림이 아닌 연산
        • 스트림의 요소를 소모하므로 단 한번만 가능 (1번)
    • 스트림의 메서드
      1. 중간 연산
        • distinct(), filter(), limit(), skip(), peek(), sorted(), mapXXX(), flatMapXXX()
      2. 최종 연산
        • forEach(), count(), max(), min(), findAny(), findfirst(), allMatch(), anyMatch(), noneMatch(), toArray(), reduce(), collect()
    • 지연된 연산: 최종 연산이 수행되기 전까지는 중간 연산이 수행되지 않음
      • 중간 연산을 호출하는 것: 어떤 작업을 수행해야하는지 지정해주는 것
    • Stream<Integer>와 IntStream
      • XXXStream: 오토/언박싱의 비효율을 줄이기 위해 데이터 소스의 요소로 기본형을 다룸
      • int형을 다루는데 유용한 메서드들이 추가적으로 존재
    • 병렬 스트림
      • 스트림의 장점: 병렬 처리가 쉬움
      • 병렬로 다루기: parallel()
      • 병렬로 처리하지 않기 (기본): sequential() 
  • 스트림 만들기
    • 컬렉션
      • 최고 조상인 Collection에 stream()이 정의되어 있음
      • 해당 컬렉션을 소스(source)로하는 스트림을 반환
    • 배열
      • Stream과 Arrays에 static method로 정의되어 있음
      • int, long, double과 같은 기본형 배열을 소스로 하는 스트림을 생성하는 메서드도 존재
    • 특정 범위의 정수
      • 지정된 범위의 연속된 정수를 스트림으로 생성해서 반환하는 함수 존재
        • range(): 경계의 끝이 포함되지 않음
        • rangeClosed(): 경계의 끝이 포함됨
    • 임의의 수: ints(), longs(), doubles()  //무한 스트림, 경계를 만들어줘야함
    • 람다식: 람다식을 매개변수로 받아서, 람다식에 의해 계산되는 값들을 요소로하는 무한 스트림 생성
      • iterate(): seed부터 시작으로 계산된 결과를 다시 seed로 이용해서 반복 (체인)
      • generate(): 이전 결과를 이용하지 않음
    • 파일: list()
    • 빈 스트림: empty()
    • 두 스트림의 연결: concat()
  • 스트림의 중간연산
    • 스트림 자르기
      • skip(): 건너뛰기
      • limit(): 수 제한하기
    • 스트림의 요소 걸러내기
      • filter(Predicate): 주어진 조건에 맞지 않는 요소를 걸러냄
      • distinct(): 중복된 요소를 제거
    • 정렬: sort()
    • 변환: map()
    • 조회: peek()
  • Optional<T> OptionalInt
    • 사용 이유: NullPointException을 발생하지 않게 하기 위해
      • Optional 객체에 담아서 반환하기 때문에
    • Optional<객체>  //사용 방법
    • Optional 값 가져오기
      • get(): null인경우 NullPointException 발생
      • orElse(): null인경우 대체할 값 지정 가능
      • orElseThrow(): 지정된 예외를 발생시킬 수 있음
  • 스트림의 최종연산
    • 스트림의 요소를 소모하는 연산: forEach()
    • 조건 검사: allMatch(), anyMatch(), noneMatch(), findFirst(), findAll()
    • 통계: count(), sum(), average(), max(), min()
    • 리듀싱: reduce()
  • collect()
    • 스트림의 최종 연산, 매개변수로 컬렉터 필요
    • 스트림을 컬렉션과 배열로 변환: toXXX()
    • 통계: counting(), summingInt(), averagingInt(), maxBy(), minBy()
    • 리듀싱: reducing()
    • 문자열 결합: joining()
    • 그룹화와 분할: groupingBy(), partitioningBy()
  • Collector 구현하기
    • supplier(): 작업 결과를 저장할 공간을 제공
    • accumulator(): 스트림의 요소를 수집할 방법을 제공
    • combiner(): 두 저장 공간을 병합할 방법을 제공(병렬스트림)
    • finiser(): 결과를 최종적으로 변환할 방법을 제공
  • 스트림의 변환
    • 스트림  →  기본형 스트림
      • mapToXXX()
    • 기본형 스트림   → 스트림
      • boxed()
      • mapToObj()
    • 기본형 스트림  →  기본형 스트림
      • asXXXStream()
    • 스트림  →  부분 스트림
      • skip()
      • limit()
    • 두 개의 스트림  →  스트림
      • concat()
    • 스트림의 스트림  →  스트림
      • flatMap()
      • flatMapXXX()
    • 스트림  →  병렬스트림
      • parallel()
      • sequential()
    • 스트림  →  컬렉션
      • collect()
    • 컬렉션  →  스트림
      • stream()
    • 스트림  →  Map
      • collect()
    • 스트림  →  배열
      • toArray()