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
이외에도 더 많은 것들이 있음.. 이러한 인터페이스들에서 람다식을 사용하는듯 하다.
(아래내용은 쓰레드에 대한 기본개념에 대한 설명이므로.. 참고)
스트림이란?
: 컬렉션, 배열 등의 컬렉션의 저장 요소를 하나씩 참조하여, 람다식으로 처리할 수 있도록 해주는 반복자(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)을 적용해 결과를 산출한다. 종료 연산은 앞에서 지정한 지연 연산이 실행되게 한다. 종료 연산을 수행한 후에는 해당 스트림을 더는 사용할 수 없다.
- 최종 연산 : 연산 결과가 스트림이 아닌 연산. 스트림의 요소를 소모하므로 단 한번만 가능. (반복, 카운트, 평균, 총합)
- 스트림의 핵심 메소드
- 자바 스트림 API 최종연산 참고자료
'JAVA' 카테고리의 다른 글
Java - Enum (0) | 2021.09.15 |
---|---|
Java - 로컬환경 자바프로젝트 세팅 (with Docker) (0) | 2021.09.15 |
Java - 자바 객체 지향의 원리와 이해(개념정리) - 2편 (0) | 2021.02.24 |
Java - 자바 객체 지향의 원리와 이해(개념정리) - 1편 (0) | 2021.02.08 |
Java - JVM(Java Virtual Machine)의 메모리 영역 - 간단 정리 (0) | 2021.02.04 |