WEEK 3-7: Java 람다, 스트림

ensalada.de.pollo·2025년 5월 3일

be

목록 보기
11/44

람다(lambda)

익명 클래스(Anonymous Class)

람다를 공부하기에 앞서서 익명 클래스에 대해 알아보아야 합니다.
익명 클래스는 말 그대로 이름이 없는 클래스를 의미합니다.
보통 한 번만 사용할 일회성 클래스를 만들 때, 별도의 파일 없이 코드 안에서 바로 정의하여 사용할 수 있습니다.
주로 인터페이스나 추상 클래스를 구현할 때 사용합니다.
여기서 람다(lambda)라는 것은, 익명 클래스를 더 간결하게 표현한 문법입니다.

public interface Calculator {
	int sum(int a, int b);
}

public class Main {
	public static void main(String[] args) {
    	// 익명 클래스로 Calculator를 구현
        // 별도의 클래스 이름 없이 바로 구현하여 변수에 할당
        Calculator calculator = new Calculator() {
        	@Override
            public int sum(int a, int b) {
            	return a + b;
            }
        };
        
        int result = calculator.sum(1, 2);
        System.out.println(result); // 3
    }
}

람다

람다는 익명 클래스를 더 간결하게 표현한 문법이라고 하였습니다. 람다는 함수형 인터페이스에서만 사용할 수 있습니다.

@FunctionalInterface
public interface Calculator {
	int sum(int a, int b);
}

public class Main {
	public static void main(String[] args) {
    	// 람다로 Calculator를 구현
        Calculator calculator = (a, b) -> a + b;
        int result = calculator.sum(1, 2);
        System.out.println(result); // 3
    }
}

함수형 인터페이스란?

추상 메서드가 1개인 인터페이스를 의미합니다.

람다식의 전달 방법

public class Main {
	public static int calculate(int a, int b, Calculator calc) {
    	return calc.sum(a, b);
    }

	public static void main(String[] args) {
    	int result;
    	// 1. 익명 클래스를 이용한 전달
    	Calculator cal1 = new Calculator() {
        	@Override
            public int sum(int a, int b) {
            	return a + b;
            }
        };
        
       	result = calculate(1, 2, cal);
        
        // 2. 람다식을 이용한 전달
        Calculator cal2 = (a, b) -> a + b;
        
      	result = calculate(1, 2, cal);
        
        // 3. 람다식을 직접 전달
        result = calculate(1, 2, (a, b) -> a + b);

람다 사용의 주의사항

  • 함수형 인터페이스만 람다로 구현이 가능합니다. 이 때 @FunctionalInterface라는 어노테이션을 붙이면 함수형 인터페이스인지 컴파일러가 확인해줍니다.
  • 오버로딩된 메서드가 있다면, 람다식 사용이 모호해져 컴파일 에러가 발생합니다.

스트림(stream)

스트림은 Java 버전 8부터 도입된 데이터 처리 도구입니다. 컬렉션이나 배열에 저장된 데이터를 효율적으로 선언할 수 있는 데이터의 흐름을 의미합니다.
즉, 데이터를 반복문 없이 한 줄로 가공, 필터링, 변환, 집계 등 다양한 작업을 할 수 있게 해줍니다.

스트림의 특징

  • 선언형으로, for문 등 명령형 코드보다 간결하고 가독성이 높습니다.
  • 파이프라인 구조로 데이터를 준비하고, 변환 또는 필터링 등의 중간 연산을 거친 뒤 최종 연산 순서로 처리합니다.
  • 내부적으로 반복을 하여, 반복문을 직접 작성하지 않고 반복을 처리합니다.
  • 한 번 사용하면 스트림은 재사용할 수 없습니다.
  • parellelStream()으로 멀티코어 병렬처리가 가능합니다.
  • 원본 데이터에 변화를 일으키지 않습니다.

스트림의 처리 단계

1. 데이터 준비

컬렉션, 배열 등에서 스트림을 생성합니다.

List<Integer> list = Arrays.asList(1, 2, 3);
Stream<Integer> stream = list.stream();

2. 중간 연산

map(), filter(), sorted() 등의 메서드를 활용합니다. 해당 메서드들의 연산 결과 또한 스트림이기 때문에 여러 번 반복해서 사용할 수도 있습니다.

stream.filter(n -> n % 2 == 0) // 짝수인 요소
	.map(n -> n * 10) // 각 요소에 10을 곱함

3. 최종 연산

collect(), forEach(), count(), redue() 등의 메서드를 활용합니다. 중간 연산 단계의 메서드들과는 다르게 연산 결과가 스트림이 아니기 때문에 한 번만 사용할 수 있습니다.

// collect
.collect(Collectors.toList());

// forEach
.forEach(System.out::println);

::는 무슨 의미인가요?

::는 메서드 참조(Method Reference) 연산자입니다.
Java 버전 8부터 도입된 문법으로, 람다식에서 기존 메서드만 호출할 때 더욱 간결하게 표현할 수 있도록 해줍니다.

위 System.out::println을 예시로 들어보면,
원래는 람다식으로

list.forEach(n -> System.out.println(n));

이렇게 작성해야하는 구문을

list.forEach(System.out::println);

이렇게 작성하는 것입니다.

즉, System.out의 println 메서드를 참조하여 람다식에서 받은 값을 그대로 전달해 호출하라는 의미를 지니고 있습니다.

사용형태

  • 정적 메서드 참조: className::methodName
    e.g. Math::abs
  • 인스턴스 메서드 참조: variableName::methodName
    e.g. System.out::println
  • 생성자 참조: className::new
    e.g. ArrayList::new

람다식이 단순히 기존 메서드 하나만 호출할 때, 불필요한 매개변수 선언 없이 코드를 더 간결하게 만들고 싶을 때 해당 연산자를 사용합니다.

자료 및 코드 출처: 스파르타 코딩클럽

0개의 댓글