개발 이론/JAVA

Lambda

jeonghoe21 2025. 3. 4. 00:15

1️⃣ 람다 표현식(Lambda Expression) 기본 문법

람다 표현식 기본 구조

(parameters) -> { statements }

람다 표현식 주요 특징

  1. 익명 함수(Anonymous Function)
    • 별도로 이름이 없는 함수를 선언하는 방식
  2. 간결한 문법
    • 불필요한 코드 제거 가능
  3. 함수형 인터페이스와 함께 사용
    • 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() 모든 요소를 하나로 축약

'개발 이론 > JAVA' 카테고리의 다른 글

Stream  (0) 2025.02.02
Collection  (0) 2025.02.02
상속  (0) 2025.02.01
객체지향이란?  (0) 2025.02.01
배열  (0) 2025.02.01