본문 바로가기
프로젝트 개발 기록/[개발] java | spring

[Java] 람다와 스트림 (Lambda & Stream)

by HelloJudy 2022. 11. 13.

람다식 (Lambda Expression)

함수(메서드)를 간단한 '식(expression)'으로 표현하는 방법
익명 함수(이름이 없는 함수, anonymous function)

 

  • 함수와 메서드의 차이
    • 근복적으로 동일. 함수는 일반적 용어, 메서드는 객체지향개념 용어
    • 함수는 클래스에 독립적, 메서드는 클래스에 종속적

 

// 반환 값이 있는 경우
(int a, int b) -> a > b ? a : b

// 매개변수의 타입이 추론 가능하면 생략가능(대부분 경우 생략가능)
(a, b) -> a > b ? a : b

 

 

✔️ 함수형 인터페이스

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

 

  • 함수형 인터페이스 타입의 참조변수로 람다식을 참조할 수 있음
MyFunction f = (a, b) -> a > b ? a : b;
int value = f.max(3, 5); // 실제로는 람다식 호출

 

예시

List<String> list = Arrays.asList("abc", "aaa", "bbb", "ccc", "aaa");
Collections.sort(list, (s1, s2) -> s2.compareTo(s1));

 

 

✔️ java.util.function 패키지

  • 자주 사용되는 다양한 함수형 인터페이스 제공
함수형 인터페이스 메서드 설명
java.lang.Runnable void run() 매개변수 X, 반환값 X
Supplier<T> T get()  -> T 매개변수 X, 반환값 O
Consumer<T> T -> void accep(T t) 매개변수 O, 반환값 X
Function<T, R> T -> R apply(T t) -> R 일반적인 함수. 하나의 매개변수를 받아서 결과를 변환
Predicate<T> T -> boolean test(T t) -> boolean 조건식을 표현하는데 사용됨.
매개변수는 하나, 반환 타입은 boolean

 

 

✔️ 메서드 참조(method reference)

하나의 메서드만 호출하는 람다식은 '메서드 참조'로 간단히 할 수 있다.
ClassName::method

 

Integer method(String s) {
	return Integer.parseInt(s);
}

// 메서드 참조로 변경
Function<String, Integer> f = Integer::parseInt;

 


스트림 (Stream)

다양한 데이터 소스를 표준화된 방법으로 다루기 위한 것
  • 데이터 소스
    • 컬렉션 (List, Set, Map), 배열  -> Stream으로 표준화하고 나면 중간 연산, 최종 연산 방식은 같음
List<Integer> list = Arrays.asList(1,2,3,4,5);
Stream<Integer> intStream = list.stream();  // 컬렉션

 

 

스트림 작업 순서

 

1. 스트림 만들기

2. 중간 연산 (0~n번)

  • 연산 결과가 스트림인 연산. 반복적으로 적용가능

3. 최종 연산 (1번) -> 결과 얻기

  • 연산 결과가 스트림이 아닌 연산. 단 한번만 적용가능 (스트림 요소를 소모)

 

스트림의 특징

  • 스트림은 데이터 소스로부터 데이터를 읽기만할 뿐 변경하지 않는다. (원본은 변경하지 않는다.)
  • 스트림은 Iterator처럼 일회용이다. (필요하면 다시 스트림을 생성해야 함.), 최종연산 하고 나면 닫힌다.
  • 최종 연산 전까지 중간연산이 수행되지 않는다. = 지연된 연산

 

  • 로또 번호 추출 (예시)
IntStream intStream = new Random().ints(1, 46);  // 1~45범위의 무한 스트림
intStream.distinct().limit(6).sorted()  // 중간 연산
		 .forEach(i -> System.out.print(i + ","));

 

  • 스트림은 작업을 내부 반복으로 처리한다.
  • 스트림의 작업을 병렬로 처리  =  병렬스트림
int sum = strStream.parallel() // 병렬 스트림으로 전환
                   .mapToInt(s -> s.length()).sum();  // 모든 문자열의 길이의 합

 

  • 기본형 스트림 - IntStream, LongStream, DoubleStream
    • 오토박싱 & 언박싱의 비효율이 제거됨. (스트림으로 작업하는데 성능을 높이고 싶을 때 사용하면 된다.)
    • Stream<Integer> 대신 IntStream사용

 

 

✔️ 스트림 생성

컬렉션

  • Collection인터페이스의 stream()으로 컬렉션을 스트림으로 변환
  • Stream<E> stream()
intStream.forEach(System.out::print);  // 스트림의 모든 요소 출력

 

배열

  • Stream<T> Stream.of(T...values) : 가변인자
  • Stream<T> Arrays.stream(T[] array)
  • Stream<T> Arrays.stream(T[] array, int startInclusive, int endExclusive) : 배열의 일부

 

특정 범위의 정수

  • IntStream IntStream.range(int begin, int end)
  • IntStream IntStream.rangeClosed(int begin, int end)

 

 

정리

// 1. 컬렉션
List<Integer> list = Arrays.asList(1,2,3,4,5);
Stream<Integer> intStream = list.stream();  // 컬렉션

// 2. 배열
Stream<String> strStream = Stream.of("a","b","c"); // 가변인자
Stream<String> strStream = Stream.of(new String[]{"a","b","c"});
Stream<String> strStream = Arrays.stream(new String[]{"a","b","c"});
Stream<String> strStream = Arrays.stream(new String[]{"a","b","c"}, 0, 3);

// 3. 특정 범위 정수
IntStream IntStream.range(int begin, int end)  // 1,2,3,4
IntStream IntStream.rangeClosed(int begin, int end) // 1,2,3,4,5

 

 

✔️ 스트림 중간 연산

 

✔️ 스트림 최종 연산

 


📌 Reference

  • 남궁 선, 「자바의 정석」, 도우출판
반응형

댓글