
프로그래머스 데브코스 3주차 진행 중, 배운 자바 문법
이번껀 내용 량도 량인데, 중요한 요소 같아서 따로 정리했다.
하나의 식으로 표현하여 훨씬 간략하게 표현이 가능하게 되며,
메서드의 이름과 반환값이 없어지므로 "익명함수"라고도 한다.
그러니까, 쉽게 말해 메소드를 "하나의 식"으로 표현한 것이다.
int sum(int a, int b) { return a + b; }
이러한 함수가 있다고 가정하자.
물론 이게 간단해서 망정이지... 따로 이름도 정하고, 따로 빼서 또 입력하고 나에겐 너무 귀찮은 일이다...ㅠㅠㅠ
이를 람다를 이용하면 이렇게 바로 사용할 수 있다.
int a = 3;
int b = 4;
int sum = ((BinaryOperator<Integer>) ((x, y) -> x + y)).apply(a, b);
// 물론 함수가 너무 간단해서... 이렇게 복잡하게 보이는거다 ㅠㅠㅠㅠ
※ 주의!: 람다식은 반드시 함수형 인터페이스에 구현해야 한다...
람다는 매개변수 화살표(->) 함수몸체로 이용하여 사용 가능하다.
게다가! 함수몸체가 단일 실행문이면 중괄호({})를 생략까지 가능하다.
근데... return 문으로만 구성되어 있으면 괄호는 무조건 필수다.
// return만 있을 경우
() -> { return 1; }
(int x) -> { return x+1; }
x -> { return x+1; }
(x, y) -> x+y
(x, y) -> { return x+y; }
// 주의! 선언된 type과 선언되지 않은 type을 같이 사용할 수는 없다
(x, int y) -> x+y
(x, final y) -> x+y
-> 단일 추상 메소드를 가진 인터페이스, 주로 람다식과 함께 사용
@FunctionalInterface
interface Math {
public int Calc(int first, int second);
}
public static void main(String[] args){
Math plusLambda = (first, second) -> first + second;
System.out.println(plusLambda.Calc(4, 2));
Math minusLambda = (first, second) -> first - second;
System.out.println(minusLambda.Calc(4, 2));
}
이런식으로 @FunctionalInterface를 통해 인터페이스를 선언한다.
혹은 이렇게 자바에서 자체적으로 지원하는 인터페이스도 있다.
| 인터페이스 | 설명 | 메소드 | 예 |
|---|---|---|---|
Customer<T> | 값을 소비 | accept(T t) | 출력 |
Supplier<T> | 값 공급 (매개변수 x, T 반환) | get() | 값 생성 |
Predicate<T> | 조건 검사 (bool 반환) | test(T t) | 펄터링 조건 |
Function<T, R> | T를 R로 변환 | apply(T t) | 변환, 매핑 |
BiFunction<T, U, R> | 두 개의 입력을 R로 변환 | apply(T t, U u) | 계산, 조합 |
UnaryOperator<T> | 동일 타입 변환 | apply(T t) | 변환, 매핑 |
BinaryOperator<T> | 동일 타입의 두 값을 결합 | apply(T t, T t2) | 연산 |
int a = 3;
int b = 4;
BinaryOperator integerSum = (x, y) -> x + y;
int sum = integerSum.apply(a, b);
System.out.println(sum);
-> 다양한 데이터를 표준화된 방법으로 다루기 위한 라이브러리
엥? 그럼 스트림이란게 정확하게 뭐죠?
스트림 (Stream)
데이터 소스를 변경하지 않고, 일련의 데이터를 연속적으로 처리할 수 있는 추상화된 데이터 흐름
Stream API를 사용하면 데이터를 필터링, Mapping, 줄이는 작업을 단계별로 지정할 수 있다.
그래서 코드를 간결하게 유지하면서도 읽기 쉽게 만든다.
1. 지연 연산(Lazy evaluation): 중간연산(Map, filter 등)은 최종 연산이 호출될 때까지 실행 x
2. 데이터 변경 x: 스트림은 데이터 소스를 변경x, 필요할 때 새로운 스트림 반환
3. 단일 사용: 스트림은 한 번 사용한 후, 재사용 x
4. 병렬 처리 지원: 쉽게 병렬 스트림으로 전환 가능, 대용량 데이터 처리 시, 성능 최적화 기능
// List -> Stream
List<String> names = Arrays.asList("Minsu", "kim", "Minjea");
Stream<String> nameStream = names.stream();
// 배열 -> Stream
String[] nameArray = {"Minsu", "kim", "Minjea"};
Stream<String> nameArrayStream = Arrays.stream(nameArray);
// 혹은 직접 생성
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);
중간 연산은 새로운 스트림을 반환한다.
그래서 연속적인 처리가 가능하다!
종류: filter(조건에 맞는 요소), map(각 요소를 반환), sorted(정렬)
// filter
names.stream()
.filter(name -> name.startsWith("M"))
.forEach(System.out::println); // "Minsu", "Minjea" 출력
// map
names.stream()
.map(String::length)
.forEach(System.out::println); // 각 이름의 길이 출력 (5, 3, 6)
// sorted
names.stream()
.sorted()
.forEach(System.out::println); // 알파벳 순서대로 출력 (Kim, Minjea, Minsu)
종류: forEach(각 요소를 소비), collect(컬렉션으로 변환), reduce(모든 요소를 결합하여 하나의 결과 반환), count(요소 개수 반환)
// collect
List<String> result = names.stream()
.filter(name -> name.startsWith("M"))
.collect(Collectors.toList());
System.out.println(result); // [Minsu, Minjea] 출력
// reduce
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.reduce(0, Integer::sum); // 초기값 0에서 시작하여 모든 요소 합산
System.out.println(sum); // 15 출력
// count
long count = names.stream()
.filter(name -> name.startsWith("M"))
.count();
System.out.println(count); // 2 출력
// 이름이 "M"로 시작하는 요소만 필터링, 정렬 후 출력
names.stream()
.filter(name -> name.startsWith("M")) // "M"로 시작하는 이름 필터링
.sorted() // 알파벳 순서대로 정렬
.forEach(System.out::println); // 필터링된 이름을 출력
Stream API의 지연 연산(Lazy evaluation) 특성 때문에, forEach()가 실행이 되어야 filter하고 sorted (중간 연산들)가 실행이 된다.