본문 바로가기

JAVA

Java - lambda와 Stream

JAVA 8에서 추가된 새로운 개념들중.. 람다식(lambda)와 스트림(Stream)에 대한 정리

람다식이란?

: 메소드를 하나의 간결한 식으로 표현한 것..함수를 변수처럼 사용할 수 있다. 익명 함수(anonymous function)라고도 불린다.

    람다는 오직 1개의 메서드만을 가지는 인터페이스(아래에서 다시 설명하는데, 이러한 인터페이스를 Funcation Interface라 한다)의 익명 클래스 구현체를 우아하게(?) 치환할 수 있는 표현법으로, 람다식은 이 인터페이스의 인스턴스가 된다.

- 람다식의 기본구조

(타입 매개변수, ..) -> { 실행문;.. };

Test test = (int num) -> {System.out.println("number: "+num);};

// 자료형 생략
Test test = (num) -> {System.out.println("number: "+num);};

// 괄호 생략 (매개변수가 1개인경우)
// 단, 매개변수가 없는경우에는 () 빈괄호가 입력되어야 한다.
Test test = num -> {System.out.println("number: "+num);};

// 매개변수가 없는경우
Test test = () -> {System.out.println("매개변수 없음");};


// 실행문이 하나인 경우, 중괄호까지 생략가능
// 단, 하나뿐인 문장이 return문이면 중괄호 생략 불가.
Test test = num -> System.out.println("number: "+num);

- 람다식의 장점

1] 코드를 간결하게 만들 수 있다.

(단, 몸체가 길어지는 경우에는 사용을 지양해야 함)

2] 가독성의 향상 (간결성과 의도의 명확성)

3] 함수를 만드는 과정없이 한번에 처리가 가능하기 때문에, 코딩시간 단축

4] 병렬프로그래밍에 유용

- 람다식의 단점

1] 디버깅이 까다로움

2] 함수 재사용이 불가능하다

3] 재귀함수에는 적합하지 않음.

 

- 예제

람다식이 적용된 예시
람다식이 적용되지 않은 예시

@FunctionalInterface 란..?

: 구현해야 할 추상 메소드가 하나만 정의된 인터페이스, 함수형 인터페이스라고 한다.

    (이 어노테이션이 선언된 인터페이스에 두개이상의 메소드가 선언되면 오류가 발생한다)

 

자바의 람다식은, 함수형 인터페이스로만 접근이 되기 때문~

그리고, 자바에서 기본적으로 제공하는 함수형 인터페이스들이 있습니다. (java.util.function - package)

  • Runnable -> 쓰레드에서 사용하는 인터페이스
  • Supplier
  • Consumer
  • Function<T, R>
  • Predicate

이외에도 더 많은 것들이 있음.. 이러한 인터페이스들에서 람다식을 사용하는듯 하다.

 

(아래내용은 쓰레드에 대한 기본개념에 대한 설명이므로.. 참고)

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

 

스트림이란?

: 컬렉션, 배열 등의 컬렉션의 저장 요소를 하나씩 참조하여, 람다식으로 처리할 수 있도록 해주는 반복자(iterator).

쉽게 풀어서 접근을 해보면.. 데이터를 쫙 펼쳐놓고, 원하는데로 데이터를 가공하여 표현하는 것.

대신 처리속도가 일반적인 처리 방식보다는 느릴 수 있다.

 

- 스트림의 특징

  • 스트림은 데이터소스를 변경하지 않음
  • 일회용으로 사용한다. 한번 사용하면 닫혀버려서, 다시 사용할 수 없다. (필요시, 스트림을 다시 생성해야함)
  • 람다식으로 요소 처리 코드를 제공
  • 병렬 처리가 쉽다
  • 중간 처리와 최종 처리 작업을 수행

예제1) 초간단 예제

스트림 적용 초간단 예제

 

위의 예제에 대한 결과

스트림의 연산은 중간연산과 최종연산으로 나누어지며, 위의 예제에서는 중간연산으로 filter(조건에 맞는 것을 찾음)가 사용되었고, 최종연산으로 collect이 사용된 케이스이다.

 

예제2) 컬렉션과 스트림을 비교

// 컬렉션: for-each 루프를 이용하는 외부 반복
List<String> names = new ArrayList<>();
     for (Dish dist : menu){ <-- 메뉴 리스트를 명시적으로 순차 반복한다.
         names.add(dish.getName()); <-- 이름을 추출해서 리스트에 추가한다.
     }
     
// 스트림 : 내부 반복

List<String> names = menu.stream()
	.map(Dish::getName) <-- map 메서드를 getName 메서드로 파라미터화해서 요리명을 추출한다.
	.collect(toList()); <-- 파이프라인을 실행한다. 반복자는 필요없다.

- 스트림의 흐름

스트림을 다룰 때는 보통 아래와 같은 흐름을 따른다.

1. 스트림을 생성한다.

 

2. 초기 스트림을 다른 스트림으로 변환하는 중간연산(intermediate operation)을 지정한다. 여러 단계로 지정 할 수 있다.

    - 중간 연산 : 연산 결과가 스트림인 연산, 스트림에 연속해서 중간 연산할 수 있음. (요소들의 매핑, 필터링, 정렬)

    중간연산의 중요한 특징은 단말 연산을 스트림 파이프라인에 실행하기 전까지는 아무 연산도 수행하지 않는다는 것 즉 게으르다lazy는 것이다. 중간연산을 합친 다음에 합쳐진 중간연산을 최종 연산으로 한 번에 처리하기 때문이다.

 

3. 최종 연산(terminal operation)을 적용해 결과를 산출한다. 종료 연산은 앞에서 지정한 지연 연산이 실행되게 한다. 종료 연산을 수행한 후에는 해당 스트림을 더는 사용할 수 없다.

    - 최종 연산 : 연산 결과가 스트림이 아닌 연산. 스트림의 요소를 소모하므로 단 한번만 가능. (반복, 카운트, 평균, 총합)

 

- 스트림의 핵심 메소드

 

자바 스트림(Stream) API 정리, 스트림을 이용한 가독성 좋은 코드 만들기(feat. 자바 람다, 함수형 프

Java Stream 자바 공부를 하면서 Stream이 무엇인지, 어떻게 사용되고 있는지 인지는 하고 있었으나 실제 코드로 타이핑해보지 않았다. 그러던 중 이번에 가볍게 API 훑어보는 식으로 공부를 하면서

jeong-pro.tistory.com

 

- 자바 스트림 API 최종연산 참고자료

 

3.자바 스트림API 최종연산

자바 스트림API중 최종연산에 대해 배워 봅니다.

dinfree.com