반응형
Notice
Recent Posts
Recent Comments
Link
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

지식조각모음

14. 람다와 스트림 본문

책/자바의 정석

14. 람다와 스트림

y00 2022. 4. 9. 21:33
반응형

람다식

메서드를 하나의 식으로 표현한 것. 익명 함수.

int[] arr = new int[5];
Arrays.setAll(arr, (i) -> (int)(Math.random() * 5) + 1);
  • 장점
    • 간결하면서 이해하기 쉬움
    • 별도의 클래스나 메서드를 생성할 필요 없음
    • 메서드를 변수처럼 다루는 것이 가능

작성법

int max(int a, int b) {
    return a > b ? a : b;
}
  1. 메서드에서 이름과 반환타입 제거
(int a , int b) -> {
    return a > b ? a : b;
}
  1. return 문을 식으로 대체
(int a , int b) ->  a > b ? a : b
  1. 추론 가능한 경우 매개변수 타입 생략
    (a , b) ->  a > b ? a : b

익명 객체

람다식은 익명 클래스의 객체와 동등하다.

// 아래 식과
(int a , int b) ->  a > b ? a : b

// 아래 식은 동등하다
new Object() {
    int max(int a, int b) {
        return a > b ? a : b;
    }
}

함수형 인터페이스

단 하나의 추상 메서드만 선언된 인터페이스

람다식은 익명 클래스의 객체와 동등하다고 했다. 그러므로 람다식도 일종의 객체인데, 이 객체를 호출하려면 익명 객체의 주소를 담는 참조변수가 있어야 한다. 객체인 람다식을 다루기 위해 인터페이스를 사용하기로 결정되었으며, 이를 위한 참조변수가 함수형 인터페이스이다.

MyFunction f = new MyFunction() {
    public int max(int a, int b) {
        return a > b ? a : b;
    }
}

이 식은 MyFunction f = (int a , int b) -> a > b ? a : b; 와 동일하며, int big = f.max(5, 3); 처럼 호출하여 사용할 수 있다. 이를 위해서 함수형 인터페이스를 아래와 같이 선언해주면 된다.

// 함수형 인터페이스에는 오직 하나의 추상 메서드만 정의되어 있어야 하며
// 이를 올바르게 작성했는지 확인하기 위한 어노테이션
@FunctionalInterface
interface MyFunction {
    public abstract int max(int a, int b);
}

이처럼 람다식을 참조변수로 다룰 수 있고, 메서드를 통해서 람다식을 주고 받을 수 있게 되었다.

java.util.function 패키지

자주 쓰이는 형식의 메서드를 함수형 인터페이스로 미리 정의해 놓았다.

함수형 인터페이스 메서드 설명
java.lang.Runnable void run() 매개변수도 없고, 반환값도 없음
Supplier<T> T get() 매개변수 없음, 반환값 T
Consumer<T> void accept(T t) 매개변수 T, 반환값 없음
Function<T, R> R apply(T t) 매개변수 T, 반환값 R
Predicate<T> boolean test(T t) 매개변수 T, 반환값 boolean
  • 매개변수가 두 개인 경우, 앞에 'Bi'가 접두사로 붙는다
  • 매개변수의 타입과 반환타입의 타입이 일치하면, UnaryOperator라고 한다.
  • 참고: T는 Type을, R은 Return Type을 의미

Predicate의 결합

여러 Predicate를 and(), or(), negate()로 연결해서 하나의 새로운 Predicate 가능

Predicate<Integer> p = i -> i < 100;
Predicate<Integer> q = i -> i < 200;
Predicate<Integer> r = i -> i % 2 == 0;
Predicate<Integer> notP = p.negate(); // i >= 100


// i >= 100 && (i < 200 || i % 2 == 0)
Predicate<Integer> all = notP.and(q.or(r));

메서드 참조

람다식이 하나의 메서드만 호출하는 경우 메서드 참조라는 방법으로 람다식을 간략히 할 수 있다.

  • 변경 방법: '클래스이름::메서드이름' 또는 '참조변수::메서드이름'으로 바꿀 수 있다.
// 1. 원래 식
Integer wrapper(String s) {
	return Integer.parseInt(s);
}

// 2. 람다식
Function<String, Integer> f = (String s) -> Integer.parseInt(s);


// 3. 메서드 참조
Function<String, Integer> f = Integer::parseInt;

스트림

'스트림'은 컬렉션이나 배열에 저장된 데이터 소스를 추상화하여 데이터 소스가 무엇이던 간에 같은 방식으로 다룰 수 있게 해준다. 이를 통해 코드의 재사용성이 높아진다.

특징

  1. 스트림은 데이터 소스를 변경하지 않는다
  2. 스트림은 일회용이다
  3. 스트림은 작업을 내부 반복으로 처리한다
    • 반복문을 메서드 내부에 숨겼다
  4. 지연된 연산
    • 최종 연산이 수행되기 전까지는 중간 연산이 수행되지 않는다. 최종 연산이 수행되어야 비로소 스트림의 요소들이 중간 연산을 거쳐 최종 연산에서 소모된다
  5. 타입별 Stream이 있다.
    • 오토방식&언박싱으로 인한 비효율을 줄이기 위해서 기본형 스트림인 IntStream, LongStream, DoubleStream 등을 제공한다.
    • Stream<Integer>보다 IntStream을 사용하는 것이 효율적이다
  6. 병렬 스트림

스트림 만들기

  1. 컬렉션
  2. 배열
  3. 임의의 수
  4. 특정 범위의 정수
  5. 람다식
  6. 파일과 빈 스트림

스트림의 연산

  • 중간연산: 연산 결과가 스트림인 연산. 스트림에 연속해서 중간 연산 할 수 있음
    • skip(long n): n만큼 건너뜀
    • limit(long maxSize): maxSize만큼 스트림 요소 제한
    • filter(Predicate<? super T> predicate): 조건에 맞지 않는 요소 걸러냄
    • distinct(): 중복 제거
    • sorted(): 기본 정렬 기준으로 정렬 OR 지정된 Comparator로 스트림 정렬
    • map(): 스트림 요소에 저장된 값 중 원하는 필드만 뽑아내거나 특정 형태로 변환
    • peek(): 스트림 요소를 소모하지 않아, filter()나 map()의 결과 확인용으로 유용
  • 최종연산: 연산 결과가 스트림이 아닌 연산. 스트림의 요소를 소모하므로 단 한번만 가능. 스트림의 요소를 소모해서 결과를 만듦
    • forEach(): 스트림의 요소를 출려핳는 용도로 많이 사용
    • 조건 검사
    • reduce(): 요소를 줄여나가면서 연산을 수행. 스트림의 모든 요소를 소모하면 그 결과를 반환
  • 예: stream.distinct().limit(5).sorted().forEach(System.out::println)

Optional<T>

T타입의 객체를 감싸는 래퍼클래스. 최종 연산의 결과를 그냥 반환하는 게 아니라 Optional 객체에 담아서 반환하면, 반환된 결과가 null인지 매번 if문으로 체크하지 않아도 됨.

 

반응형

' > 자바의 정석' 카테고리의 다른 글

16. 네트워킹  (0) 2022.04.14
15. 입출력  (0) 2022.04.09
13. 스레드  (2) 2022.03.30
12. 제네릭스, 열거형, 애너테이션  (0) 2022.03.13
Comparator와 Comparable  (1) 2022.03.10