[java]함수형 인터페이스, 람다식&Stream (2)

송어·2023년 11월 20일

람다식(Lambda)

자바의 람다식함수형 프로그래밍에서 사용되는 함수를 간결하게 표현하기 위한 방법 중 하나이다.
람다식은 자바8부터 도입되었으며, 람다식은 익명함수(anonymous function)의 한 형태로서, 메서드 구현을 간결하게 표현하는 방법이다.

람다식은 다음과 같은 구조로 이루어진다.

(parameters) -> {expression} // 함수의 이름이 없다.(익명 함수)

위 구조에서 parameters메서드에서 사용할 매개변수, expression메서드의 구현체를 나타낸다.

예를 들어 2개의 정수를 더하는 메서드를 람다식으로 구현하면 다음과 같이 작성할 수 있다.

(int x, int y) -> {return x + y;}

람다식은 함수형 인터페이스와 함께 사용된다. 특히 java.util.function패키지에 많은 함수형 인터페이스가 정의되어 있어 이 인터페이스들을 람다식으로 구현해 사용할 수 있다.

다음은 함수형 인터페이스를 람다식으로 구현한 예시이다.

@FunctionalInterface // 함수형 인터페이스
public interface MathOperation {
    public int operation(int x, int y);
}
public class LambdaTest {
    public static void main(String[] args) {

//        MathOperation add = (int x, int y) -> { return x + y; };
        MathOperation add = (x, y) -> x + y; // 타입 및 return 생략 가능

        int result = add.operation(10, 20);
        System.out.println("result = " + result);
    }
}

람다식을 사용하면 코드가 줄고 간편하게 인터페이스를 구현할 수 있어서 확장성도 커진다.

public class LambdaTest {
    public static void main(String[] args) {

        MathOperation add = (x, y) -> x + y; // 코드가 줄어 간편하게 구현할 수 있고, 확장이 쉬워진다.
        MathOperation multi = (x, y) -> x * y;
        MathOperation minus = (x, y) -> x - y;
        MathOperation divid = (x, y) -> x / y;

        int result1 = add.operation(60, 30); 
        int result2 = multi.operation(60, 30);
        int result3 = minus.operation(60, 30);
        int result4 = divid.operation(60, 30);

        System.out.println("result1 = " + result1);
        System.out.println("result2 = " + result2);
        System.out.println("result3 = " + result3);
        System.out.println("result4 = " + result4);
    }
}

람다식 사용방법

람다식을 메서드 내에서 사용하거나 메서드의 인자로 전달할 수도 있다.

public class LambdaApplyTest {
    public static void main(String[] args) {
        // 람다식으로 모든 문자를 대문자로 변환하는 StringOperation 구현
        StringOperation operationUpperCase = s -> s.toUpperCase();
        StringOperation operationLowerCase = s -> s.toLowerCase();

        String input = "Lambda Expresstions";

        System.out.println(processString(input, operationUpperCase)); // operationUpperCase람다식 전달
        System.out.println(processString(input, operationLowerCase)); // operationLowerCase람다식 전달

    }

    public static String processString(String input, StringOperation operation) { // 람다식을 메서드의 인자로 전달
        return operation.apply(input); // 해당 메서드 내에서 람다식이 사용되게 된다.
    }
}

각각 구현한 람다식을 따로 사용하지 않고, 메서드의 인자로 람다식을 전달해 코드 수정을 용이하게 만들고 확장성을 높였다.

스트림(Stream)

스트림은 자바8에서 도입된 기능으로, 데이터의 흐름을 다루기 위한 선언형 API이다,
스트림을 사용해 필터링, 매핑, 정렬 등 다양한 데이터 처리 작업을 적용할 수 있으며, 최종 결과를 배열이나 컬렉션으로 변환할 수 있다. 또한, 스트림은 데이터 처리 작업을 연속적인 파이프라인으로 나타낼 수 있어 가독성이 높고, 병력처리를 쉽게 구현할 수 있다.

배열을 스트림으로 변환하기

배열을 스트림으로 변환한다는 것은 배열의 원소들을 스트림 형태로 변환하여 처리할 수 있게 하는 것이다. 스트림은 원본 데이터를 변경하지 않고, 필요한 데이터 처리 작업을 적용한 결과를 생설하기 때문에 인덱스를 통한 접근은 제공하지 않는다.

1. Arrays.stream() 메서드 사용

int[] numbers = {1,2,3,4,5};
intStream stream = Arrays.stream(numbers);

위 코드에서 numbers 배열을 스트림으로 변환한 뒤 StreamAPI에서 제공하는 메서드를 활용해 다양한 방식의 처리를 할 수 있다.

int sumOfEvens = Arrays.stream(numbers) // 배열을 스트림으로 변환 후 1. 짝수 원소만 골라 2. 합계를 계산
					   .filter(n -> n % 2 == 0)
                       .sum();
  1. 스트림을 배열이나(toArray()) 컬렉션으로(toCollect()) 변환
int[] numbers = {1,2,3,4,5,6,7,8,9,10};

int[] evenNumbers = Arrays.stream(numbers) // 배열을 스트림으로 변환 후 1. 짝수 원소만 골라 2. 배열로 변환
						  .filter(n -> n % 2 == 0)
                          .toArray();

StreamAPI 활용

스트림API와 함수형 인터페이스를 사용해 List에 저장된 정수들의 짝수 여부를 판별하고, 짝수들만 필터링하여 정렬하고, 각 숫자를 제곱한 후 모든 숫자의 합을 계산해보자

public class StreamExampleTest {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(3,5,6,9,7,4,1,10,8,2);

        //Predicate<Integer> isEven = n -> n % 2 == 0;

        int sumOfSquares = list.stream()
                                .filter(StreamExampleTest::isEven)
                                .sorted()
                                .map(n -> n * n)
                                .reduce(0, Integer::sum);

        System.out.println("짝수의 제곱의 합 = " + sumOfSquares);
    }

    public static boolean isEven(int number) {
        return number % 2 == 0;
    }
}

정수 리스트에서 각 원소를 제곱한 값을 출력

public class MapStreamTest {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1,3,5,4,2);
        List<Integer> squaredNumbers = numbers.stream()
                .sorted()
                .map(n -> n * n)
                .collect(Collectors.toList());

        System.out.println("squaredNumbers = " + squaredNumbers);
    }
}

스트림의 문자열 원소를 대문자로 변환

public class MapStreamTest2 {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("apple", "banana", "cherry", "orange");

        List<String> uppercase = words.stream()
                .map(String::toUpperCase)
                .collect(Collectors.toList());

        System.out.println("uppercase : " + uppercase);

        for(String str : uppercase) {
            System.out.println("str : " + str);
        }
    }
}

0개의 댓글