람다식 보충자료
함수형 인터페이스
(1) 함수형 인터페이스란?
정의: 추상 메서드가 하나만 존재하는 인터페이스로, 함수형 프로그래밍을 지원하기 위해 사용.
특징:
@FunctionalInterface
애노테이션:함수형 인터페이스임을 명시적으로 선언.
추상 메서드가 2개 이상일 경우 컴파일 오류 발생.
익명 함수를 실행하기 위한 껍데기 역할:
함수형 프로그래밍에서 람다 표현식과 함께 사용.
메서드가 하나만 있기 때문에, 람다 표현식을 통해 간결하게 표현 가능.
(2) 함수형 인터페이스 예제
(1) 직접 함수형 인터페이스 작성
코드 예시
@FunctionalInterface
interface Calculator {
int calculate(int a, int b); // 추상 메서드 하나만 선언
}
public class FunctionalInterfaceExample {
public static void main(String[] args) {
// 람다 표현식으로 구현
Calculator add = (a, b) -> a + b; // 더하기
Calculator subtract = (a, b) -> a - b; // 빼기
System.out.println("Addition: " + add.calculate(5, 3)); // 8
System.out.println("Subtraction: " + subtract.calculate(5, 3)); // 2
}
}
실행 결과
Addition: 8
Subtraction: 2
메서드 참조
(1) 메서드 참조란?
정의: 람다식에서 메서드 하나만 호출하는 경우, 메서드 참조를 통해 동일하게 표현할 수 있습니다.
표현 방식:
ClassName::methodName
또는instance::methodName
.
(2) 메서드 참조 예제
(1) 정적 메서드 참조
람다식 표현:
(i) -> Integer.valueOf(i)
메서드 참조:
Integer::valueOf
코드 예시
import java.util.function.Function;
public class StaticMethodReferenceExample {
public static void main(String[] args) {
// 람다식
Function<String, Integer> parseIntLambda = str -> Integer.valueOf(str);
System.out.println("Parsed using lambda: " + parseIntLambda.apply("123"));
// 메서드 참조
Function<String, Integer> parseIntMethodRef = Integer::valueOf;
System.out.println("Parsed using method reference: " + parseIntMethodRef.apply("123"));
}
}
실행 결과
Parsed using lambda: 123
Parsed using method reference: 123
(2) 인스턴스 메서드 참조
람다식 표현:
(s) -> s.toUpperCase()
메서드 참조:
String::toUpperCase
코드 예시
import java.util.function.Function;
public class InstanceMethodReferenceExample {
public static void main(String[] args) {
// 람다식
Function<String, String> toUpperCaseLambda = str -> str.toUpperCase();
System.out.println("Uppercase using lambda: " + toUpperCaseLambda.apply("hello"));
// 메서드 참조
Function<String, String> toUpperCaseMethodRef = String::toUpperCase;
System.out.println("Uppercase using method reference: " + toUpperCaseMethodRef.apply("hello"));
}
}
실행 결과
Uppercase using lambda: HELLO
Uppercase using method reference: HELLO
스트림 (Stream)
1. 스트림이란?
정의: 배열 또는 컬렉션 데이터를 여러 방식으로 처리할 수 있는 API.
특징:
원본 데이터를 수정하지 않음: 스트림은 데이터를 복사하여 처리하며, 원본 데이터는 변경되지 않음.
선언적 스타일: 데이터를 처리하는 방식이 간결하고, 가독성이 높음.
병렬 처리 가능: 스트림은 데이터의 병렬 처리를 쉽게 수행.
중간 연산과 최종 연산:
중간 연산: 필터링, 정렬 등(새로운 스트림 반환).
최종 연산: 결과를 반환하거나 출력.
다양한 데이터 타입 지원: 기본형(
int
,double
)과 래퍼 클래스(Integer
,Double
) 모두 가능.
2. 스트림 생성 및 활용 예제
(1) 배열을 스트림으로 변환
코드 예시
import java.util.Arrays;
import java.util.stream.IntStream;
public class ArrayToStreamExample {
public static void main(String[] args) {
int[] numbers = {3, 6, 2, 9, 8, 4};
// 스트림 생성 및 처리
IntStream stream = Arrays.stream(numbers);
// 짝수만 필터링하여 내림차순 정렬 후 출력
stream.filter(n -> n % 2 == 0) // 짝수 필터링
.sorted() // 정렬(기본 오름차순)
.forEach(System.out::println); // 출력
}
}
실행 결과
2
4
6
8
(2) 리스트 데이터를 스트림으로 변환
코드 예시
import java.util.Arrays;
import java.util.List;
public class ListToStreamExample {
public static void main(String[] args) {
List<String> fruits = Arrays.asList("Apple", "Banana", "Cherry");
// 스트림 생성 및 출력
fruits.stream()
.map(String::toUpperCase) // 모든 문자열을 대문자로 변환
.forEach(System.out::println);
}
}
실행 결과
APPLE
BANANA
CHERRY
(3) 스트림끼리 합치기
코드 예시
import java.util.stream.Stream;
public class StreamMergeExample {
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("A", "B", "C");
Stream<String> stream2 = Stream.of("X", "Y", "Z");
// 스트림 병합
Stream<String> mergedStream = Stream.concat(stream1, stream2);
// 결과 출력
mergedStream.forEach(System.out::println);
}
}
실행 결과
A
B
C
X
Y
Z
(4) JSON 객체 배열 처리
코드 예시
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
public class JsonStreamExample {
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("Alice", 25),
new User("Bob", 30),
new User("Charlie", 35)
);
// 나이가 30 이상인 사용자 필터링
List<String> names = users.stream()
.filter(user -> user.getAge() >= 30) // 조건 필터링
.map(User::getName) // 이름만 추출
.collect(Collectors.toList()); // 결과 리스트로 변환
System.out.println(names);
}
}
실행 결과
[Bob, Charlie]
3. 스트림의 다양한 메서드
(1) 빈 스트림 생성
코드 예시
import java.util.stream.Stream;
public class EmptyStreamExample {
public static void main(String[] args) {
// 빈 스트림 생성
Stream<String> emptyStream = Stream.empty();
// 결과 확인
System.out.println("Is stream empty? " + emptyStream.count()); // 0
}
}
(2) 기본형 스트림
코드 예시
import java.util.stream.IntStream;
public class PrimitiveStreamExample {
public static void main(String[] args) {
// 기본형 스트림 생성
IntStream.range(1, 5) // 1부터 4까지의 정수 스트림
.forEach(System.out::println);
}
}
실행 결과
1
2
3
4
(3) 스트림의 중간 및 최종 연산
연산
설명
예시
filter()
조건에 맞는 요소 필터링
stream.filter(n -> n > 5)
map()
요소를 다른 형태로 변환
stream.map(n -> n * n)
sorted()
요소를 정렬 (기본 오름차순)
stream.sorted()
distinct()
중복 제거
stream.distinct()
forEach()
각 요소에 대해 작업 수행 (최종 연산)
stream.forEach(System.out::println)
collect()
요소를 수집하여 리스트, 집합 등으로 반환
stream.collect(Collectors.toList())
reduce()
요소를 하나의 값으로 축소
stream.reduce(0, Integer::sum)
count()
요소의 개수 반환 (최종 연산)
stream.count()
concat()
두 스트림을 합침
Stream.concat(stream1, stream2)
4. 정리
스트림의 주요 특징
원본 데이터 수정 없음: 스트림은 데이터를 복사하여 처리.
선언적 스타일: 데이터를 처리하는 방식이 간결하고 가독성이 높음.
중간 연산과 최종 연산: 데이터 변환과 결과 처리로 분리.
병렬 처리 지원: 대량 데이터를 효율적으로 처리 가능.
자주 사용하는 스트림 메서드
filter
: 조건에 맞는 데이터 추출.map
: 데이터를 변환.collect
: 결과를 리스트, 집합 등으로 수집.forEach
: 데이터를 순회하며 처리.
Last updated