[java] 새로운 computeIfAbsent 함수를 어떻게 사용합니까?

나는 Map.computeIfAbsent 를 사용하고 싶지만 학부에서 람다 이후로 너무 오래되었습니다.

문서에서 거의 직접 : 작업을 수행하는 이전 방법의 예를 제공합니다.

Map<String, Boolean> whoLetDogsOut = new ConcurrentHashMap<>();
String key = "snoop";
if (whoLetDogsOut.get(key) == null) {
  Boolean isLetOut = tryToLetOut(key);
  if (isLetOut != null)
    map.putIfAbsent(key, isLetOut);
}

그리고 새로운 방법 :

map.computeIfAbsent(key, k -> new Value(f(k)));

그러나 그들의 예에서 나는 “그것을 이해”하고 있지 않다고 생각합니다. 이것을 표현하는 새로운 람다 방식을 사용하도록 코드를 어떻게 변환할까요?



답변

다음 코드가 있다고 가정합니다.

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Test {
    public static void main(String[] s) {
        Map<String, Boolean> whoLetDogsOut = new ConcurrentHashMap<>();
        whoLetDogsOut.computeIfAbsent("snoop", k -> f(k));
        whoLetDogsOut.computeIfAbsent("snoop", k -> f(k));
    }
    static boolean f(String s) {
        System.out.println("creating a value for \""+s+'"');
        return s.isEmpty();
    }
}

그러면 해당 키에 대한 값이 이미있는 creating a value for "snoop"두 번째 호출 에서 메시지가 정확히 한 번만 표시됩니다 computeIfAbsent. k람다 표현은 k -> f(k)바로지도가 값을 계산하기 위해 람다로 전달됩니다 키에 대한 placeolder (매개 변수)입니다. 따라서 예제에서 키는 함수 호출로 전달됩니다.

또는 다음과 같이 작성할 수 whoLetDogsOut.computeIfAbsent("snoop", k -> k.isEmpty());있습니다. 도우미 메서드없이 동일한 결과를 얻을 수 있습니다 (하지만 디버깅 출력은 표시되지 않음). 또한 기존 메소드에 대한 간단한 위임이므로 더 간단합니다. whoLetDogsOut.computeIfAbsent("snoop", String::isEmpty);이 위임은 매개 변수를 작성할 필요가 없습니다.

으로 가까운 예에 질문에 있으려면, 당신은 그것을 쓸 수 whoLetDogsOut.computeIfAbsent("snoop", key -> tryToLetOut(key));(당신이 매개 변수의 이름을 여부를 중요하지 않습니다 k또는 key). 또는로 쓰는 whoLetDogsOut.computeIfAbsent("snoop", MyClass::tryToLetOut);경우 tryToLetOut입니다 static또는 whoLetDogsOut.computeIfAbsent("snoop", this::tryToLetOut);경우는 tryToLetOut인스턴스 방법이다.


답변

최근에 저도이 방법을 가지고 놀았습니다. 나는 방법을 사용하는 방법에 대한 또 다른 예시가 될 수있는 피보나치 수를 계산하는 메모 알고리즘을 작성했습니다.

우리는지도를 정의하고 기본 경우에 그것의 값을 넣어 시작할 수 있습니다, 즉, fibonnaci(0)fibonacci(1):

private static Map<Integer,Long> memo = new HashMap<>();
static {
   memo.put(0,0L); //fibonacci(0)
   memo.put(1,1L); //fibonacci(1)
}

귀납적 단계를 위해 우리가해야 할 일은 다음과 같이 피보나치 함수를 재정의하는 것입니다.

public static long fibonacci(int x) {
   return memo.computeIfAbsent(x, n -> fibonacci(n-2) + fibonacci(n-1));
}

보시 computeIfAbsent다시피이 메서드 는 제공된 람다 식을 사용하여 숫자가지도에 없을 때 피보나치 수를 계산합니다. 이는 기존의 트리 재귀 알고리즘에 비해 상당한 개선을 나타냅니다.


답변

다른 예시. 복잡한지도지도를 만들 때 computeIfAbsent () 메서드는지도의 get () 메서드를 대체합니다. computeIfAbsent () 호출을 함께 연결하면 누락 된 컨테이너가 제공된 람다 식에 의해 즉석에서 생성됩니다.

  // Stores regional movie ratings
  Map<String, Map<Integer, Set<String>>> regionalMovieRatings = new TreeMap<>();

  // This will throw NullPointerException!
  regionalMovieRatings.get("New York").get(5).add("Boyhood");

  // This will work
  regionalMovieRatings
    .computeIfAbsent("New York", region -> new TreeMap<>())
    .computeIfAbsent(5, rating -> new TreeSet<>())
    .add("Boyhood");


답변

다중지도

당신이 만들 경우에 정말 유용 과 multimap을 받는 의지하지 않고 구글 구아바 의 구현을위한 라이브러리 MultiMap.

예를 들어 특정 과목에 등록한 학생 목록을 저장한다고 가정합니다.

JDK 라이브러리를 사용하는 일반적인 솔루션은 다음과 같습니다.

Map<String,List<String>> studentListSubjectWise = new TreeMap<>();
List<String>lis = studentListSubjectWise.get("a");
if(lis == null) {
    lis = new ArrayList<>();
}
lis.add("John");

//continue....

상용구 코드가 있기 때문에 사람들은 Guava를 사용하는 경향이 있습니다 Mutltimap.

Map.computeIfAbsent를 사용하면 다음과 같이 guava Multimap없이 한 줄로 작성할 수 있습니다.

studentListSubjectWise.computeIfAbsent("a", (x -> new ArrayList<>())).add("John");

Stuart Marks와 Brian Goetz는 이것에 대해 좋은 이야기를했습니다.
https://www.youtube.com/watch?v=9uTVXxJjuco


답변