호우동의 개발일지

Today :

article thumbnail

썸네일

Java에서 Map을 사용하다 보면 로직을 처리하는데
함수 호출이 잦아지는 경우가 많아 코드를 한눈에 알아보기가 힘들다.

그래서 Map interface 중 Map 사용을 간결하게 하도록 돕는 함수에 대해 알아봤다.

이번 포스팅에서 소개할 메서드는

compute()
computeIfAbsent() vs putIfAbsent()
computeIfPresent() 이다.

소개도 나와있는 순서대로 하겠다.

 


compute


동작

compute(K key, BiFunction <? super K,? super V,? extends V> remappingFunction)

동작 순서는 다음과 같다.

1. key의 존재 유무와 상관없이 람다식(remappingFunction)을 수행한다.

2-1. key에 값이 존재 O → NullPointerException 발생
2-2. key에 값이 존재 O + 람다식의 결과가 Null → 해당 key를 삭제
2-3. key에 값이 존재 O + 람다식의 결과가 Null이 아님oldValue를 해당 key로 매핑 및 반환

 


예제 코드

public static void main(String[] args) 
{
	Map<String, Integer> map = new HashMap<>();

    // map 안에 키 A,B를 넣는다.
    map.put("A", 0);
    map.put("B", 0);

    // 키가 존재 + 람다식이 null이 아닌 경우
    Integer returnValue = map.compute("A", (key, oldValue) -> oldValue + 1);

    // 키가 존재 + 람다식이 null인 경우
    map.compute("B", (key, oldValue) -> oldValue = null);
	
    // compute의 반환값은 람다식의 결과값
    System.out.println(returnValue);
    System.out.println(map.get("A"));
    System.out.println("B가 있나요? " + map.containsKey("B"));
}

출력
출력

oldValue 0에서 +1을 한 값이 A의 새로운 값이 됐다.

또한 compute() 메서드의 리턴값은
람다식의 계산값이라는 것을 returnValue 변수로 확인할 수 있다.

B에서는 람다식의 결과가 Null이기 때문에 B가 삭제됐다.
때문에 map에서 containsKey()를 하면 false가 나오는 것을 확인할 수 있다.

public static void main(String[] args) {
    Map<String, Integer> map = new HashMap<>();
    
    // 키가 존재하지 않을 경우
    map.compute("C", (key, oldValue) -> oldValue + 1);
}

NullPointerException 발생
NullPointerException 발생

마지막으로 존재하지 않는 키를 가지고 compute()를 할 경우,
NullPointerException이 나오는 것을 확인할 수 있다.

 


computeIfAbsent vs putIfAbsent


computeIfAbsent 동작

computeIfAbsent(K key, Function <? super K,? extends V> mappingFunction)

이름으로 어느 정도 유추가 가능한데, 쉽게 말해서
키가 존재하지 않을 경우, compute()를 실행하는 메서드이다.

함수형 인터페이스가 Function이기 때문에 (key) -> {} 식으로 작성해야 한다.

동작은 다음과 같다.

- key에 값이 존재 O → 람다식 실행 X, 해당 key에 매핑된 값을 반환
- key에 값이 존재 X람다식 실행 O, 람다식 수행 값을 key에 매핑하고 반환


computeIfAbsent 예제 코드

public static void main(String[] args) {
    //new Controller(new InputView(new InputValidator()), new OutputView()).run();
    Map<String, String> map = new HashMap<>();

    //"A"란 키가 없기 때문에 람다식이 실행되고 key "A"가 생성됨
    String a = map.computeIfAbsent("A", key -> key = "ABC");
    System.out.println(a);
    System.out.println(map.get("A"));
    
    System.out.println("--------------");
    
    //"A"란 키가 있기 때문에 람다식 실행X, "A"에 있던 값이 리턴됨
    String b = map.computeIfAbsent("A", key -> "DEF");
    System.out.println(b);
    System.out.println(map.get("A"));
}

출력
출력

위 예제 코드에서 주목해야 할 점은
똑같이 computeIfAbsent()를 출력했는데, 다른 결과가 나왔다는 점

처음에 호출했을 때는 map에 "A"라는 key가 없었기 때문에 람다식이 실행된다.

그래서 "A"라는 키가 생성됨과 동시에 "ABC"라는 값이 들어간 뒤, String a에 "ABC"가 반환된다.

두 번째로 호출했을 때는, map에 "A"라는 key가 이미 존재하기 때문에
람다식이 수행되지 않아 "DEF"가 key의 값으로 갱신되지 않는다.

결과적으로 String b에 키 "A"의 값인 "ABC"가 반환되는 것을 확인할 수 있다.

 


putIfAbsent 동작

putIfAbsent(K key, V value)

그렇다면 putIfAbsent는 뭐가 다를까? 사실 computeIfAbsent랑 비슷하다.
차이점은 computeIfAbsent가 함수라면, putIfAbsent는 변수라는 것이다.

- key에 값이 존재 O → 해당 key에 있는 기존 값을 반환
- key에 값이 존재 X value를 key에 매핑한다. 하지만 반환 값은 null

computeIfAbsent는 key 값이 존재하지 않아도 람다식의 계산값으로 반환해 주지만,
putIfAbsent의 경우 null을 반환한다.

 


computeIfAbsent 예제 코드

public static void main(String[] args) {
    Map<String, String> map = new HashMap<>();
    
    //"A"란 키가 없기 때문에 "ABC"가 "A"키가 생성됨
    String a = map.putIfAbsent("A", "ABC");
    
    System.out.println("a = " + a); // 리턴 값 = null
    System.out.println(map.get("A"));

    System.out.println("--------------");
    
    //"A"란 키가 존재하므로 그대로 "ABC"
    String b = map.putIfAbsent("A", "DEF");
    
    System.out.println("b = " + b); // 리턴값 "ABC"
    System.out.println(map.get("A"));
}

출력
출력

 


computeIfPresent


동작

computeIfPresent(K key, BiFunction <? super K,? super V,? extends V> mappingFuction)

computeIfAbsent()와 반대로 동작하는 메서드이다.
해당 key가 존재할 때만 람다식을 수행한다고 생각하면 된다.

computeIfPresent와의 차이점은 함수형 인터페이스가 다르다는 점
해당 메서드는 (key, value) -> { } 식으로 코드를 작성해야 한다.

정확한 동작은 아래와 같다.

- key에 값이 존재 O 람다식 수행 O, 람다식 수행 값을 key에 매핑하고 반환
- key에 값이 존재 X 람다식 수행 X, Null 반환

 


예제 코드

public static void main(String[] args) {
    Map<String, String> map = new HashMap<>();
    map.put("A", "A");
    
    //"A"란 키가 있기 때문에 람다식이 실행됨
    String a = map.computeIfPresent("A", (key, value) -> "ABC");
    System.out.println("a = " + a);
    System.out.println(map.get("A"));

    System.out.println("--------------");
    
    //"B"란 키가 없기 때문에 null이 반환
    String b = map.computeIfPresent("B", (key, value) -> "DEF");
    
    System.out.println("b = " + b);
    System.out.println(map.get("B"));
}

출력
출력

computeIfAbsent와는 반대로 동작하는 것을 확인할 수 있다.

차이점이라면 해당 메서드는 key가 존재하지 않는다고 해서 생성해주지 않는다.
그냥 null을 반환하고 종료한다.

 

https://codingnojam.tistory.com/39

 

[Java] Map Interface의 유용한 메서드를 알아보자! (Java 8 기준)

안녕하세요 coding-knowjam입니다. 오늘은 Map Interface의 메서드 중 Java8에서 등장한 유용한 메서드들을 알아보겠습니다. Java8 하면 빼놓을 수 없는 게 람다식인데 오늘 알아볼 메서드들은 대부분 람다

codingnojam.tistory.com