Ch.14 람다와 스트림
- 람다식
- 람다식이란
- 의미: 메서드를 하나의 식(expression)으로 표현한 것(익명 함수)
- 사용 이유: 함수를 간략하고 명확하게 표현 가능
- 메서드와 마찬가지로 매개변수를 받을 수도 있고, 값을 반환할 수 있음
- 람다식 작성하기
- (매개변수 선언) -> {}
- return 대신 식으로 대신하므로 연산 결과가 자동으로 반환됨 (';'붙이지 않음)
- 매개변수 타입이 추론 가능하므로 생략 가능
- 매개변수가 하나뿐인 경우(매개변수 타입이 없는 경우): 괄호() 생략 가능
- 괄호{} 안의 문장이 하나인 경우: 생략 가능
- 함수형 인터페이스(Functional Interface)
- 람다식을 다루기 위한 인터페이스
- 함수형 인터페이스에는 오직 하나의 추상 메서드만 정의되어 있어야함
- @FunctionalInterface: 컴파일러가 함수형 인터페이스를 올바르게 작성했는지 확인함
- 람다식을 참조변수로 다룰수 있다는 것의 의미: 메서드를 통해 람다식을 주고받을 수 있다는 의미
- 람다식의 타입과 형변환
- 타입은 있지만 컴파일러가 임의로 이름을 정하기 때문에 알 수 없으므로 대입 연산자의 양변의 타입을 일치시키기 위해 형변환 필수
- 함수형 인터페이스로만 형변환 가능
- java.util.function 패키지
- 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 미리 정의함
- 사용 이유:
- 함수형 인터페이스에 정의된 메서드 이름 통일
- 재사용성이나 유지보수에 용이
- 자주 쓰이는 함수형 인터페이스 (매개변수 1개)
- Supplier<T>
- T get(): 매개변수 없고, 반환값 존재
- Consumer<T>
- void accept(T t): 매개변수 있고, 반환값 없음 (Supplier와 반대)
- Function<T>
- R apply(T t): 매개변수와 반환값 존재, 일반적인 함수, 하나의 매개변수로 결과를 반환
- Predicate<T>
- boolean test(T t): 조건식을 표현하는데 사용, 매개변수는 하나, 반환타입은 boolean
- Supplier<T>
- 자주 쓰이는 함수형 인터페이스 (매개변수 2개) - 접두사 Bi
- BiConsumer<T, U>
- void accept(T t, U u): 두 개의 매개변수만 있고, 반환값 없음
- BiPredicate<T, U>
- boolean test(T t, U u): 조건식을 표현하는데 사용, 매개변수는 둘, 반환값 존재
- BiFunction<T,U,R>
- R apply(T t, U u): 두 개의 매개변수를 받아서 하나의 결과를 반환
- BiConsumer<T, U>
- UnaryOperator과 BinaryOperator: 매개변수의 타입과 반환타입의 타입이 모두 일치한다는 점을 제외하면 Function과 같음
- 컬렉션 프레임웍과 함수형 인터페이스
- 컬렉션 프레임웍의 인터페이스에 일부는 함수형 인터페이스를 사용
- 기본형을 사용하는 함수형 인터페이스
- 효율적으로 처리할 수 있도록 기본형을 사용하는 함수형 인터페이스
- Function의 합성과 Predicate의 결합
- Function의 합성
- 두 람다식을 합성해서 새로운 람다식을 만드는 것
- 순서 중요
- Predicate의 결합
- 여러 Predicate를 and(), or(), negate()로 연결해서 새로운 Predicate로 결합 가능
- Function의 합성
- 메서드 참조
- 메서드 참조(method reference): 람다식을 더 간결하게 표현하는 방법
- 하나의 메서드만 출력하는 람다식은 '클래스이름::메서드이름' 또는 '참조변수::메서드이름'으로 표현 가능
- 생성자의 메서드 참조: 클래스이름::new
- 스트림(stream)
- 스트림이란
- 데이터 소스를 추상화하고, 데이터를 다루는데 자주 사용되는 메서드들을 정의해놓음
- 데이터 소스의 추상화 의미: 데이터 소스가 무엇이든지 같은 방식으로 다룰 수 있음(유지보수성)
- 특징
- 원본 데이터 소스를 변경하지 않음
- 일회용임
- 작업을 내부 반복으로 처리
- 스트림의 연산
- 중간 연산:
- 연산 결과가 스트림
- 스트림에 연속해서 중간연산을 할 수 있음 (n번)
- 최종 연산:
- 연산 결과가 스트림이 아닌 연산
- 스트림의 요소를 소모하므로 단 한번만 가능 (1번)
- 중간 연산:
- 스트림의 메서드
- 중간 연산
- distinct(), filter(), limit(), skip(), peek(), sorted(), mapXXX(), flatMapXXX()
- 최종 연산
- 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(): 지정된 예외를 발생시킬 수 있음
- 사용 이유: NullPointException을 발생하지 않게 하기 위해
- 스트림의 최종연산
- 스트림의 요소를 소모하는 연산: 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()
- 스트림 → 기본형 스트림
'JAVA 기초' 카테고리의 다른 글
[JAVA 기초] JAVA의 정석 - Ch.13 쓰레드(Thread) (정리) (2) | 2025.01.23 |
---|---|
[JAVA 기초] JAVA의 정석 - Ch.12 지네릭스, 열거형, 애너테이션 (정리) (3) | 2025.01.22 |
[JAVA 기초] JAVA의 정석 - Ch.11 컬렉션 프레임웍 (정리) (1) | 2025.01.21 |
[JAVA 기초] JAVA의 정석 - Ch.10 날짜와 시간 & 형식화 (정리) (4) | 2025.01.17 |
[JAVA 기초] JAVA의 정석 - Ch.09 Java.lang 패키지와 유용한 클래스 (정리) (2) | 2025.01.16 |