본문 바로가기
블로그 이미지

방문해 주셔서 감사합니다! 항상 행복하세요!

  
   - 문의사항은 메일 또는 댓글로 언제든 연락주세요.
   - "해줘","답 내놔" 같은 질문은 답변드리지 않습니다.
   - 메일주소 : lts06069@naver.com


Java(자바)/Java 기본

재미있고 어려운 Java의 람다(lambda)

야근없는 행복한 삶을 위해 ~
by 마샤와 곰 2022. 4. 7.

 

자바에서 람다는 기존의 인터페이스형태의 클래스를 화살표모양의 함수 형태로 표기하는 방법 입니다.

이러한 람다식의 장점은 아래와 같습니다.

 

1. 코드의 간결성 - 람다를 사용하면 불필요한 반복문의 삭제가 가능하며 복잡한 식을 단순하게 표현할 수 있습니다.
2. 지연연산 수행 - 람다는 지연연상을 수행 함으로써 불필요한 연산을 최소화 할 수 있습니다.
3. 병렬처리 가능 - 멀티쓰레디를 활용하여 병렬처리를 사용 할 수 있습니다.

 

일반적으로 표기하는 람다식 샘플 입니다.

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class LambdaTest {

    public static void main(String[] args) {
		
        List<Integer> list = Arrays.asList(1,2,3,4,5);
        list.stream().map( arg -> arg*2).forEach(System.out::println);
        
    }
        
}

 

스트림(stream) 객체에서 map 메소드를 실행 하였습니다.

map은 스트림 객체의 값을 변화시킬 때 사용하는 메소드 입니다.

이와같은 람다식을 쓰지 않고 자바7 이하의 버전으로 표기하려면 아래처럼 해야 합니다.

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class LambdaTest {

    public static void main(String[] args) {
		
        List<Integer> list = Arrays.asList(1,2,3,4,5);
        list.stream().map( arg -> arg*2).forEach(System.out::println);
        
        //자바7 이하로 표기
        list.stream().map( new Function<Integer, Integer>() {  //map람다를 풀어썼습니다.
            @Override
            public Integer apply(Integer t) {
                return t * 2;
            }
        }).forEach(new Consumer<Integer>() { //foreach에서의 이중콜른 표기법을 풀어 썼습니다.
            @Override
            public void accept(Integer t) {
                System.out.println(t);
            }
        });     
        
    }
        
}

 

코드가 딸랑 2줄인 내용이 위 샘플코드처럼 방대해진 것을 볼 수 있습니다.

이러한 람다식은 화살표 모양으로도 사용할 수 있으면서, 메소드 형태로 분리하여 대입하는 방법으로도 사용 할 수 있습니다.

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class LambdaTest {

    public static void main(String[] args) {
		
        List<Integer> list = Arrays.asList(1,2,3,4,5);
        list.stream().map( testMethod(aaa->aaa)).forEach(System.out::println);	    
        
    }
        
    public static Function<Integer,Integer> testMethod(Function<Integer, Integer> fun){
        return arg ->{
            return fun.apply(arg) * 2;  //위 map 객체의 람다식을 메소드로 추가 정의하여 반환합니다.
        };
    }
    
}

 

map객체에서의 람다식을 testMethod 라는 메소드에서 받아서 다시 람다식으로 리턴하는 모습 입니다.

Function으로 반환한 이유는, map객체에서의 인자값은 Function으로 되어 있습니다.

 

그런데 이러한 메소드로 람다식을 다시 정의하는 방법은 조금 주의해야되는 점이 있습니다.

위 메소드를 간단하게 고쳐보겠습니다.

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class LambdaTest {

    public static void main(String[] args) {
		
        List<Integer> list = Arrays.asList(1,2,3,4,5);
        list.stream().map( testMethod(aaa->aaa)).forEach(System.out::println);	    
        
    }
        
    public static Function<Integer,Integer> testMethod(Function<Integer, Integer> fun){
        int number[] = {2};
        return arg ->{
            return fun.apply(arg) * number[0]++; 
        };
    }
    
}

 

위 코드를 실행하면 몇몇 사람들이 "2, 4, 6, 8, 10" 이 출력될꺼야 라고 생각합니다.

왜냐하면 testMethod 메소드가 map 메소드에서 호출 될 때 마다 실행되는 것이라고 생각하기 때문에 number배열의 값은 항상 2로 초기화되어 존재할 것이라 생각 할 수 있기 때문 입니다.

 

그런데 이를 실행하면 아래와 같은 모습이 나옵니다.

에...? number배열 값이 증가했습니다?

 

무슨 상황인지 파악하기 위해서 간단한 출력코드를 입력 해 봅니다.

testMethod는 한번 호출되었고 return 형태로 정의된 함수식만 리스트 크기에 맞게 호출되었습니다.

 

testMethod는 계속되서 사용되지 않았습니다.

testMethod에서 리턴한 함수식만 리스트의 크기에 맞게 map 메소드에 의해서 사용되는 것을 볼 수 있습니다.

 

이처럼 람다식을 사용할 때 메소드에 의해서 다시 함수로 리턴하는 방법은 메소드 영역의 변수들이 마치 공유 되듯 사용되는 것을 볼 수 있습니다.

이를 응용하면 아래 포스팅처럼 List형태의 값에서 데이터 중복제거하는 방법을 적용 할 수 있습니다.

https://lts0606.tistory.com/180

 

JAVA List 중복제거, distinct (stream과 Function을 활용)

자바 1.8을 활용한 데이터 중복제거 방법 입니다. List가 포함하는 데이터는 HashMap으로 하였습니다. 물론, 사용자가 만든 vo 객체를 사용해도 무방하며, 일반 String, Integer, float, char 등 다른 자료형

lts0606.tistory.com

공부 할수록, 알면 알 수록 어렵고 재밌는 람다!

좀더 햇갈리지 않도록 열심히 노력해야겠습니다.

 

 

 

반응형
* 위 에니메이션은 Html의 캔버스(canvas)기반으로 동작하는 기능 입니다. Html 캔버스 튜토리얼 도 한번 살펴보세요~ :)
* 직접 만든 Html 캔버스 애니메이션 도 한번 살펴보세요~ :)

댓글