람다식(Lambda Expression) 정리
람다식이란?
- 메소드를 하나의 식(expression)으로 표현한 것입니다.
람다식으로 표현하면 메소드의 이름과 반환값이 없어지므로, 익명함수라고도 합니다. - 함수명을 선언하고 사용하는 것이 아닌 식별자 없이 실행 가능한 함수입니다.
- 절차형 프로그래밍, 객체지향 프로그래밍과는 사뭇 다르게 함수의 구현과 호출만으로 프로그램을 만드는 방식인 함수형 프로그래밍에서 자주 사용됩니다.
- 자바 8부터 도입 되었으며, 람다식을 익명함수로 만드는 것인데 익명함수는 일급객체 특징을 가지고 있습니다.
- 람다식 사용 예시 => (매개변수, ...) -> {실행문...}
익명함수란?
- 익명함수란 말 그대로 함수의 이름이 없는 함수입니다.
익명함수들은 공통으로 일급객체(First Class Citizen)라는 특징을 가지고 있습니다. - 이 일급객체란 일반적으로 다른 객체들에 적용 가능한 연산을 모두 지원하는 개체를 가르킵니다.
함수를 값으로 사용할 수도 있으며 파라미터로 전달 및 변수에 대입하기와 같은 연산들이 가능합니다.
일급객체란?
- 일반적으로 다른 객체들에 적용 가능한 연산을 모두 지원하는 객체입니다.
함수를 값으로 사용할 수도 있으며 파라미터로 전달 및 변수에 대입하기와 같은 연산들이 가능합니다.- 일급객체의 조건
- 변수나 데이터 구조 안에 담을 수 있습니다.
- 파라미터로 전달할 수 있습니다.
- 반환값으로 사용할 수 있습니다.
- 할당에 사용된 이름과 무관하게 고유한 구별이 가능합니다.
- 함수형 프로그래밍에서 함수는 1급 객체로 취급받기 때문에 함수를 파라미터로 넘기는 등의 작업이 가능한 것입니다.
또한 우리가 일반적으로 알고 개발했던 함수들은 함수형 프로그래밍에서 정의하는 순수 함수들과는 다르다는 것을 인지해야 합니다.
- 일급객체의 조건
package study.lambda;
import java.util.Arrays;
public class lambda1 {
public static void main(String[] args) {
// 람다식 변환 예시
int [] arr = new int [5];
Arrays.setAll(arr, (i) -> (int)(Math.random()*5)+1); // 기본 람다식
// ===== 이 람다식을 메소드로 표현하면 =====
int lambda() {
return (int)(Math.random()*5)+1;
}
}
}
람다식의 장단점
장점
- 코드의 간결성 - 람다를 사용하면 불필요한 반복문의 삭제가 가능하며 복잡한 식을 단순하게 표현할 수 있습니다.
- 자연연산 수행 - 람다는 자연연산을 수행 함으로써 불필요한 연산을 최소화 할 수 있습니다.
- 병렬처리 가능 - 멀티쓰레드를 활용하여 병렬처리를 사용할 수 있습니다.
단점
- 람다식의 호출이 까다롭습니다.
- 람다 stream 사용 시 단순 for문 혹은 while문 사용 시 성능이 떨어집니다.
- 불필요하게 너무 사용하게 되면 오히려 가독성이 떨어질 수 있습니다.
람다식 작성해보기
- 람다식은 매개변수 화살표 (->) 함수 몸체로 이용하여 사용할 수 있습니다.
- 함수몸체가 단일 실행문이면 괄호 {}를 생략할 수 있습니다.
- 함수 몸체가 return문으로만 구성되어 있는 경우 괄호 {}를 생략할 수 없습니다.
//정상적인 유형
() -> {}
() -> 1
() -> { return 1; }
(int x) -> x+1
(x) -> x+1
x -> x+1
(int x) -> { return x+1; }
x -> { return x+1; }
(int x, int y) -> x+y
(x, y) -> x+y
(x, y) -> { return x+y; }
(String lam) -> lam.length()
lam -> lam.length()
(Thread lamT) -> { lamT.start(); }
lamT -> { lamT.start(); }
//잘못된 유형 선언된 type과 선언되지 않은 type을 같이 사용 할 수 없다.
(x, int y) -> x+y
(x, final y) -> x+y
람다식 예제
- 인자 x, y를 받아들여 x,y의 계산식을 리턴하는 람다식 만들기
package study.lambda;
// 인자 x, y를 받아들여 사칙연산 리턴하는 람다식 만들기
// 함수형 인터페이스
@FunctionalInterface
interface MyFunction {
int calc(int x, int y);
}
public class Lambda2 {
public Lambda2() {
MyFunction f = new MyFunction() {
@Override
public int calc(int x, int y) {
return x + y;
}
};
System.out.println("f의 결과 : " + f.calc(100, 100));
MyFunction f1 = (x, y) -> { return x+y;};
System.out.println("f1의 결과 : " + f1.calc(10, 90));
MyFunction f2 = (x, y) -> {return x-y;};
System.out.println("f2의 결과 : " + f2.calc(90, 10));
}
public static void main(String[] args) {
new Lambda2();
}
}
package study.lambda;
//인자 x, y를 받아들여 사칙연산 리턴하는 람다식 만들기
//함수형 인터페이스
@FunctionalInterface
interface MyFunction1 {
int calc1(int x);
}
@FunctionalInterface
interface MyFunction2 {
int calc2(int x, int y);
}
public class Lambda3 {
public Lambda3() {
MyFunction1 Square1 = (x) -> { return x * x; };
System.out.println("Square1의 결과 : " + Square1.calc1(2));
MyFunction1 Square2 = (x) -> { return x * x * x; };
System.out.println("Square2 의 결과 : " + Square2.calc1(4));
MyFunction2 Square3 = (x, y) -> { return x * y; };
System.out.println("Square3의 결과 : " + Square3.calc2(6, 8));
}
public static void main(String[] args) {
new Lambda3();
}
}
- 매개변수 없는 람다식 만들기
package study.lambda;
// 매개변수가 없는 람다식 만들기
// 함수형 인터페이스
@FunctionalInterface
interface MyFunction3 {
void print();
}
public class Lambda4 {
public Lambda4 () {
MyFunction3 print = () -> {
System.out.println("Lambda Developer");
};
print.print();
}
public static void main(String[] args) {
new Lambda4();
}
}
- 메소드 인자로 매개변수 전달
package study.lambda;
// 메소드 인자로 람다식 전달
// 함수형 인터페이스
@FunctionalInterface
interface MyFunction4 {
int calc (int x, int y);
}
public class Lambda5 {
// 메소드 정의 (메소드의 인자로 람다식 전달)
static void printMultiply (int x, int y, MyFunction4 f) {
System.out.println("실행 결과 : " + f.calc(x, y));
}
public Lambda5() {
printMultiply(50, 50, (x, y) -> { return x * y; });
}
public static void main(String[] args) {
new Lambda5();
}
}
- 제네릭을 이용한 함수형 인터페이스
package study.lambda;
// 제네릭을 이용한 함수형 인터페이스
@FunctionalInterface
interface MyFunction5 <T> {
void println(T x);
}
public class Lambda6 {
public Lambda6() {
MyFunction5<String> f1 = x -> {
System.out.println(x.toString());
};
f1.println("String Generic 사용한 람다식");
f1.println("Lambda Developer");
MyFunction5<Integer> f2 = x -> {
System.out.println(x.toString());
};
f2.println(Integer.valueOf(100));
}
public static void main(String[] args) {
new Lambda6();
}
}
Stream API이란
- Stream이란 다양한 데이터를 표준화된 방법으로 다루기 위한 라이브러리입니다.
자바 8부터 추가된 Stream API는 다음과 같이 구성됩니다.
example.stream().filter(x -> x < 2).count
- stream() <- 스트림 생성
filter <- 중간 연산 (스트림 변환) - 연속으로 수행 가능합니다.
count <- 최종 연산 (스트림 사용) - 마지막에 단 한 번만 사용 가능합니다.
Stream의 특징
- Stream은 데이터를 변경하지 않습니다.
- Stream은 1회용 입니다.
- Stream은 지연 연산을 수행합니다.
- Stream은 병렬 실행이 가능합니다.
Stream의 종류
Stream<T> | 법용 Stream |
IntStream | 값 타입이 Int인 Stream |
LongStream | 값 타입이 Long인 Stream |
DoubleStream | 값 타입이 Double인 Stream |
Stream의 중간 연산 명령어
Stream < T > distinct | Stream의 요소 중복 제거 |
Stream < T > sorted () | Stream 요소 정렬 |
Stream < T > filter (Predicate < T > predicate) | 조건에 충족하는 요소를 Stream으로 생성 |
Stream < T > limit (long maxSize) | maxSize까지의 요소를 Stream으로 생성 |
Stream < T > skip (long n) | 처음 n개의 요소를 제외하는 Stream 생성 |
Stream < T > peek (Consumer < T > action) | T타입 요소에 맞는 작업 수행 |
Stream < R > flatMap (Function < T, stream<? extends R>> Tmapper) |
T타입 요소를 1:N의 R타입 요소로 변환하여 스트림 생성 |
Stream < R > map (Function <? super T, ? extends R> mapper |
입력 T타입을 R타입 요소로 변환한 스트림 생성 |
Stream mapToInt(), mapToLong(), mapToDouble() | 만약 map Type이 숫자가 아닌 경우 변환하여 사용 |
Stream의 최종 연산 명령어
void forEach(Consumer <? super T> action) | Stream 의 각 요소에 지정된 작업 수행 |
long count() | Stream 의 요소 개수 |
Optional < T > sum (Comparator <? super T> comparator) | Stream 의 요소 합 |
Optional < T > max (Comparator <? super T> comparator) | Stream 요소의 최대 값 |
Optional < T > min (Comparator <? super T> comparator) | Stream 요소의 최소 값 |
Optional < T > findAny() | Stream 요소의 랜덤 요소 |
Optional < T > findFirst() | Stream 의 첫 번째 요소 |
boolean allMatch(Pradicate < T > p) | Stream 의 값이 모두 만족하는지 boolean 반환 |
boolean anyMatch(Pradicate < T > p) | Stream 의 값이 하나라도 만족하는지 boolean 반환 |
boolean noneMatch(Pradicate < T > p) | Stream 의 값이 하나라도 만족하지않는지 boolean 반환 |
Object[] toArray() | Stream 의 모든 요소를 배열로 반환 |
reduce 연산 | Stream 의 요소를 하나씩 줄여가며 계산한다. |
- Optional < T > reduce(Binary Operator<T> accumulator) - T reduce ( T identity, BinaryOperator<T> accumulator) - <U> U reduce (U indentity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner) |
- .reduce((x,y) -> x > y ? x : y ); - .reduce(1, (x,y) -> x * y); - .reduce(0.0, (val1, val2) -> Double.valueOf(val1 + val2 / 10), (val1, val2) -> val1 + val2); |
collector 연산 | Stream의 요소를 수집하여 요소를 그룹화 하거나 결과를 담아 반환하는데 사용한다. |
- Collectors.toList() - Collectors.toSet() - Collectors.toMap() - Collectors.groupingBy - Collectors.partioningBy - Collectors.summarizingInt() |
Stream API 예제
package study.lambda;
import java.util.stream.IntStream;
public class Stream1 {
public static void main(String[] args) {
IntStream.range(1, 11).filter(i -> i % 2 == 0).forEach(System.out::println);
}
}
package study.lambda;
import java.util.stream.IntStream;
public class Stream2 {
public static void main(String[] args) {
// 0 ~ 1000까지의 값 중 500 이상 & 짝수 & 5의 배수인 수의 합을 구하라
System.out.println(
IntStream.range(0, 1001)
.skip(500)
.filter(i -> i % 2 == 0)
.filter(i -> i % 5 == 0)
.sum());
}
}
'JAVA' 카테고리의 다른 글
[JAVA] - JPA란 / Hibernate란 (0) | 2023.11.10 |
---|---|
[JAVA] - 문자열을 정수로 변환 (String to Int) (0) | 2023.10.18 |
[JAVA] - Comparable과 Comparator의 차이 (0) | 2023.10.16 |
[JAVA] - 배열 (0) | 2023.09.29 |
[JAVA] - 자바 기본 용어 2 (0) | 2023.09.27 |