Java Stream과 Lambda 완벽 가이드
Java Stream과 Lambda
Java 8에서 도입된 함수형 프로그래밍 기능인 Stream API와 Lambda 표현식을 살펴봅니다.
Lambda 표현식
기본 문법
(argument-list) -> {body}
인터페이스 구현
익명 클래스 대신 Lambda를 사용하여 간결하게 표현할 수 있습니다.
interface Drawable {
public void draw();
}
익명 클래스:
Drawable d = new Drawable() {
public void draw() {
System.out.println("Drawing " + width);
}
};
Lambda:
Drawable d2 = () -> {
System.out.println("Drawing " + width);
};
다중 파라미터
interface Addable {
int add(int a, int b);
}
public class LambdaExpressionExample {
public static void main(String[] args) {
// 타입 추론 사용
Addable ad1 = (a, b) -> (a + b);
System.out.println(ad1.add(10, 20)); // 30
// 명시적 타입 선언
Addable ad2 = (int a, int b) -> (a + b);
System.out.println(ad2.add(100, 200)); // 300
}
}
forEach 루프
List<String> list = new ArrayList<>();
list.add("ankit");
list.add("mayank");
list.add("irfan");
list.add("jai");
list.forEach(n -> System.out.println(n));
메서드 참조
정적 메서드 참조
ContainingClass::staticMethodName
interface Sayable {
void say();
}
public class MethodReference {
public static void saySomething() {
System.out.println("Hello, this is static method.");
}
public static void main(String[] args) {
Sayable sayable = MethodReference::saySomething;
sayable.say();
}
}
스레드에서 활용:
Thread t2 = new Thread(MethodReference::ThreadStatus);
t2.start();
다중 파라미터:
class Arithmetic {
public static int add(int a, int b) {
return a + b;
}
}
BiFunction<Integer, Integer, Integer> adder = Arithmetic::add;
int result = adder.apply(10, 20); // 30
인스턴스 메서드 참조
containingObject::instanceMethodName
interface Sayable {
void say();
}
public class MethodReference {
public void saySomething() {
System.out.println("Hello, this is non-static method.");
}
public static void main(String[] args) {
MethodReference methodReference = new MethodReference();
Sayable sayable = methodReference::saySomething;
sayable.say();
// 익명 객체 사용
Sayable sayable2 = new MethodReference()::saySomething;
sayable2.say();
}
}
생성자 참조
ClassName::new
interface Messageable {
Message getMessage(String msg);
}
class Message {
public Message(String msg) {
System.out.print(msg);
}
}
public class ConstructorReference {
public static void main(String[] args) {
Messageable hello = Message::new;
hello.getMessage("Hello");
}
}
Stream flatMap
map 연산이 하나의 요소를 하나의 요소로 변환하는 반면, flatMap은 하나의 요소를 여러 개의 요소(또는 0개)로 변환할 수 있습니다.
계층 구조 예시
class Foo {
String name;
List<Bar> bars = new ArrayList<>();
Foo(String name) {
this.name = name;
}
}
class Bar {
String name;
Bar(String name) {
this.name = name;
}
}
데이터 생성
List<Foo> foos = new ArrayList<>();
// Foo 객체 생성
IntStream
.range(1, 4)
.forEach(i -> foos.add(new Foo("Foo" + i)));
// Bar 객체 생성
foos.forEach(f ->
IntStream
.range(1, 4)
.forEach(i -> f.bars.add(new Bar("Bar" + i + " <- " + f.name))));
flatMap 사용
foos.stream()
.flatMap(f -> f.bars.stream())
.forEach(b -> System.out.println(b.name));
// 출력:
// Bar1 <- Foo1
// Bar2 <- Foo1
// Bar3 <- Foo1
// Bar1 <- Foo2
// Bar2 <- Foo2
// Bar3 <- Foo2
// Bar1 <- Foo3
// Bar2 <- Foo3
// Bar3 <- Foo3
3개의 Foo 객체가 9개의 Bar 객체로 변환됩니다.
한 줄로 표현하기
IntStream.range(1, 4)
.mapToObj(i -> new Foo("Foo" + i))
.peek(f -> IntStream.range(1, 4)
.mapToObj(i -> new Bar("Bar" + i + " <- " + f.name))
.forEach(f.bars::add))
.flatMap(f -> f.bars.stream())
.forEach(b -> System.out.println(b.name));
Optional의 flatMap
flatMap은 Optional 클래스에서도 사용할 수 있어 null 체크를 우아하게 처리할 수 있습니다.
중첩 구조 예시
class Outer {
Nested nested;
}
class Nested {
Inner inner;
}
class Inner {
String foo;
}
기존 방식 (null 체크)
Outer outer = new Outer();
if (outer != null && outer.nested != null && outer.nested.inner != null) {
System.out.println(outer.nested.inner.foo);
}
Optional flatMap 사용
Optional.of(new Outer())
.flatMap(o -> Optional.ofNullable(o.nested))
.flatMap(n -> Optional.ofNullable(n.inner))
.flatMap(i -> Optional.ofNullable(i.foo))
.ifPresent(System.out::println);
각 flatMap 호출은 객체가 존재하면 해당 객체를 감싸는 Optional을, 없으면 빈 Optional을 반환합니다.
정리
| 개념 | 설명 |
|---|---|
| Lambda | 익명 함수를 간결하게 표현 |
| 메서드 참조 | 기존 메서드를 Lambda 대신 사용 |
| 생성자 참조 | 생성자를 메서드 참조처럼 사용 |
| map | 1:1 변환 |
| flatMap | 1:N 변환 (스트림 평탄화) |
| Optional.flatMap | null-safe한 중첩 객체 접근 |
Comments