1️⃣ 람다 표현식(Lambda Expression) 기본 문법
✅ 람다 표현식 기본 구조
(parameters) -> { statements }
✅ 람다 표현식 주요 특징
- 익명 함수(Anonymous Function)
- 별도로 이름이 없는 함수를 선언하는 방식
- 간결한 문법
- 불필요한 코드 제거 가능
- 함수형 인터페이스와 함께 사용
- FunctionalInterface를 구현하는 메서드를 간략하게 표현 가능
📌 Java 함수형 인터페이스(Functional Interface)란?
- Java에서 람다 표현식을 사용할 때 필요한 인터페이스입니다.
- 함수형 인터페이스는 추상 메서드가 단 하나만 존재해야 합니다.
- @FunctionalInterface 어노테이션을 붙이면 해당 인터페이스가 함수형 인터페이스임을 보장합니다.
✅ 함수형 인터페이스의 주요 종류
함수형 인터페이스메서드설명
| Runnable | run() | 매개변수 없음, 반환값 없음 |
| Supplier<T> | get() | 매개변수 없음, 반환값 있음 |
| Consumer<T> | accept(T t) | 매개변수 있음, 반환값 없음 |
| Function<T, R> | apply(T t) | 입력을 받아 변환 후 반환 |
| Predicate<T> | test(T t) | 입력을 받아 true 또는 false 반환 |
| BiFunction<T, U, R> | apply(T t, U u) | 두 개의 입력을 받아 변환 후 반환 |
1-1. Runnable - run()
📌 매개변수 없음, 반환값 없음
- 별도의 인자를 받지 않고 단순히 실행하는 작업을 정의할 때 사용
📝 예제
Runnable r = () -> System.out.println("Runnable 실행!");
r.run(); // 출력: Runnable 실행!
🔍 run() 메서드의 역할
- Runnable 인터페이스에서 제공
- 스레드 실행 시 사용됨
1-2 Consumer<T> - accept(T t)
📌 매개변수 있음, 반환값 없음
- 주어진 입력을 소비(사용)하고 반환값이 필요 없을 때 사용
📝 예제
Consumer<String> consumer = name -> System.out.println("Hello, " + name);
consumer.accept("Alice"); // 출력: Hello, Alice
🔍 accept(T t) 메서드의 역할
- 입력을 받아서 처리하지만, 결과를 반환하지 않음
- System.out.println()처럼 출력하는 경우 유용
3️⃣ Function<T, R> - apply(T t)
📌 입력을 받아 변환 후 반환
- 입력값을 받아 다른 타입으로 변환해야 할 때 사용
📝 예제
Function<Integer, String> function = num -> "숫자: " + num;
System.out.println(function.apply(10)); // 출력: 숫자: 10
🔍 apply(T t) 메서드의 역할
- 입력을 받아서 변환 후 반환하는 작업을 수행
- 스트림에서 map()과 함께 자주 사용됨
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
List<String> stringNumbers = numbers.stream()
.map(num -> "숫자: " + num) // Function<T, R> 적용
.collect(Collectors.toList());
System.out.println(stringNumbers); // 출력: [숫자: 1, 숫자: 2, 숫자: 3, 숫자: 4]
4️⃣ Predicate<T> - test(T t)
📌 입력을 받아 true 또는 false 반환
- 조건 검사를 수행할 때 사용
📝 예제
Predicate<Integer> isEven = num -> num % 2 == 0;
System.out.println(isEven.test(4)); // 출력: true
System.out.println(isEven.test(5)); // 출력: false
🔍 test(T t) 메서드의 역할
- 입력값이 특정 조건을 만족하는지 검사
- 스트림의 filter()와 함께 자주 사용됨
List<Integer> evenNumbers = numbers.stream()
.filter(num -> num % 2 == 0) // Predicate<T> 적용
.collect(Collectors.toList());
System.out.println(evenNumbers); // 출력: [2, 4]
5️⃣ BiFunction<T, U, R> - apply(T t, U u)
📌 두 개의 입력을 받아 변환 후 반환
- 두 개의 값을 입력받아 결과를 반환해야 할 때 사용
📝 예제
BiFunction<Integer, Integer, Integer> sum = (a, b) -> a + b;
System.out.println(sum.apply(3, 5)); // 출력: 8
🔍 apply(T t, U u) 메서드의 역할
- 두 개의 입력을 받아 하나의 결과를 반환
- map()과 함께 복합 변환을 수행할 때 유용
6️⃣ Supplier<T> - get()
📌 매개변수 없이 값을 반환
- 값을 제공하는 역할을 함
📝 예제
Supplier<Double> randomSupplier = () -> Math.random();
System.out.println(randomSupplier.get()); // 랜덤 값 출력
🔍 get() 메서드의 역할
- 외부에서 값을 받아오지 않고 생성해야 할 때 사용
- 지연 초기화(lazy initialization)에 활용 가능
2️⃣ 람다 표현식 활용 예제
1) 기본적인 람다 표현식
// 1. 매개변수가 없는 경우
Runnable r = () -> System.out.println("Hello, Lambda!");
r.run();
// 2. 매개변수가 하나인 경우
Consumer<String> consumer = name -> System.out.println("Hello, " + name);
consumer.accept("Alice");
// 3. 매개변수가 여러 개인 경우
BiFunction<Integer, Integer, Integer> sum = (a, b) -> a + b;
System.out.println(sum.apply(3, 5)); // 출력: 8
// 4. 코드 블록이 있는 경우
BiConsumer<String, Integer> printer = (name, age) -> {
System.out.println("이름: " + name);
System.out.println("나이: " + age);
};
printer.accept("Alice", 25);
3️⃣ Java Stream과 람다 표현식
Java Stream API는 컬렉션(List, Set, Map) 데이터를 함수형 스타일로 조작할 수 있도록 도와줍니다.
✅ 스트림 기본 구조
collection.stream() // 컬렉션에서 스트림 생성
.filter(조건) // 특정 조건을 만족하는 요소만 필터링
.map(변환) // 요소 변환
.sorted() // 정렬
.collect(Collectors.toList()); // 리스트로 변환
4️⃣ filter()를 이용한 데이터 필터링
📌 filter()는 특정 조건을 만족하는 요소만 추출하는 기능을 합니다.
예제 1: 리스트에서 짝수만 추출
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> evenNumbers = numbers.stream()
.filter(num -> num % 2 == 0)
.collect(Collectors.toList());
System.out.println(evenNumbers); // 출력: [2, 4, 6, 8, 10]
예제 2: 이름 리스트에서 "A"로 시작하는 이름 필터링
List<String> names = Arrays.asList("Alice", "Bob", "Andrew", "Charlie");
List<String> filteredNames = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
System.out.println(filteredNames); // 출력: [Alice, Andrew]
5️⃣ map()을 이용한 데이터 변환
📌 map()은 스트림의 각 요소를 변환하여 새로운 스트림을 생성하는 기능을 합니다.
예제 3: 모든 숫자를 제곱 값으로 변환
List<Integer> squaredNumbers = numbers.stream()
.map(num -> num * num)
.collect(Collectors.toList());
System.out.println(squaredNumbers); // 출력: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
예제 4: 문자열 리스트에서 길이 변환
List<Integer> nameLengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println(nameLengths); // 출력: [5, 3, 6, 7]
6️⃣ sorted()를 이용한 정렬
📌 sorted()는 요소를 오름차순 또는 내림차순 정렬하는 기능을 합니다.
예제 5: 숫자 리스트 정렬
List<Integer> sortedNumbers = numbers.stream() .
sorted()
.collect(Collectors.toList());
System.out.println(sortedNumbers); // 출력: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
예제 6: 문자열 리스트를 사전순으로 정렬
List<String> sortedNames = names.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(sortedNames); // 출력: [Alice, Andrew, Bob, Charlie]
예제 7: 내림차순 정렬
List<Integer> descendingNumbers = numbers.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
System.out.println(descendingNumbers); // 출력: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
7️⃣ collect()를 이용한 리스트 변환
📌 collect()는 스트림의 결과를 리스트, 집합, 맵 등으로 변환하는 기능을 합니다.
예제 8: 리스트 변환
List<String> collectedNames = names.stream()
.filter(name -> name.length() > 3)
.collect(Collectors.toList());
System.out.println(collectedNames); // 출력: [Alice, Andrew, Charlie]
예제 9: Set으로 변환
Set<String> uniqueNames = names.stream()
.collect(Collectors.toSet());
예제 10: Map으로 변환
Map<String, Integer> nameLengthMap = names.stream()
.collect(Collectors.toMap(name -> name, String::length));
System.out.println(nameLengthMap); // 출력: {Alice=5, Bob=3, Andrew=6, Charlie=7}
8️⃣ reduce()를 이용한 데이터 축소
📌 reduce()는 모든 요소를 하나로 축약하는 기능을 합니다.
예제 11: 숫자 리스트의 합 계산
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b);
System.out.println(sum); // 출력: 55
예제 12: 가장 긴 문자열 찾기
Optional<String> longestName = names.stream()
.reduce((name1, name2) -> name1.length() > name2.length() ? name1 : name2);
longestName.ifPresent(System.out::println); // 출력: Charlie
🔍 람다 & 스트림 메서드 정리
메서드설명
| stream() | 리스트, 배열을 스트림으로 변환 |
| filter() | 특정 조건을 만족하는 요소만 필터링 |
| map() | 요소를 변환하여 새로운 스트림 생성 |
| sorted() | 정렬 수행 |
| findAny() | 아무 요소 하나 찾기 (병렬) |
| findFirst() | 첫 번째 요소 찾기 (순차) |
| collect() | 결과를 리스트, 집합, 맵 등으로 변환 |
| reduce() | 모든 요소를 하나로 축약 |