java에서 computeifabsent랑, computeifpresent는 꽤 유용하게 쓸 수 있는 Map 메소드입니다. 이 중에서, 저는 후자를 위주로 설명하도록 하겠습니다.

 


 먼저, computeifpresent 메서드에 대한 설명을 보겠습니다.

 

 어려울 것은 없고요. key와 Bifunction을 받습니다. key가 이미 있는 경우에는, key와 value를 가지고 새롭게 mapping하는 것을 시도하는 함수입니다. 그런데 이 설명만 보아서는 어떤 일을 하는 지 알기가 쉽지 않아 보입니다. 예제를 작성해 보겠습니다.

 

 

 먼저, 3가지 연산을 한 후에, map에 있는 내용을 출력하게끔 하였습니다. 결과를 보고 이야기 해 보겠습니다.

 

 

 먼저, 처음에 맵은 비어 있었을 겁니다. 2를 추가하려고 하니 없었으므로, 람다식에 의해서, apply가 된 결과물인 new Integer(5)를 Value 값으로, Key값을 2로 해서 맵에 넣습니다. 만약에 키 값이 있다면, 키 값에 대응되는 value 값을 돌려줍니다. 그래서, 이 연산을 수행하고 나서 {2=5}가 나왔습니다.

 

 그 다음에, computeIfPresent 메서드에서 key값이 2였습니다. 그리고 bifunction으로 (k,v)->v*v를 넘겨 줬는데요. 이는 value값의 제곱을 적용해서 돌려준다는 의미입니다. 키 값이 2인 것의 value 값은 5였으므로, 이 인터페이스에 의해서 apply가 되고 난 후에는 25가 될 겁니다. 따라서, 2=25가 됩니다.

 

 

 실제로, computeIfPresent에 있는 apply 메서드인데요. t와 u는 각각 first, second argument를 의미하는데요. 맵에서 key, value에 대응됩니다. 설명을 보면, 해당 함수를 적용을 한다고 되어 있고, return 하는 값은 함수의 결과값이라고 되어 있습니다. 즉, 8번째 줄과 같이 넘기면, v*v를 적용한 결과를 돌려주게 됩니다. 키 값이 2일 때, v 값은 25이므로, 25를 돌려주게 됩니다.

 

 

 정말 그런지는 HashMap 클래스의 해당 메서드를 보면 알 수 있습니다. key와 oldValue를 각각 1번째, 2번째 인자로 넣어버리고 있어요. Main 클래스의 8번째 줄에 있는 해당 메서드의 2번째 인자와 대응시켜 보면, key값이 k, oldValue 값이 v로 대응 되었음을 알 수 있습니다. 그리고, 이 oldValue는 해당 메서드가 적용되기 전 key값이 2인 value 값인 5를 의미합니다.

 


 문제는, null 값을 주었을 때 왜 키가 제거 되었느냐는 겁니다. 이것도 해당 메서드를 보면 알 수 있습니다.

 

 

 해당 메서드는 Key와 BiFunction 인터페이스로 이루어져 있습니다.

 

 

 그리고, 이 BiFunction은 2개의 argument를 받습니다. 내부를 보면, removeNode라는 것이 보입니다.

 

 

 이 부분인데요. key 값이 있으면서, value 값이 null이 아닌 경우에 if문 블록 안으로 들어오게 됩니다. 1147번째 줄에 보면, v가 있는데요. key와 oldValue 2개를 받아서 apply한 결과가 null이라면, else가 수행되는데요. else문에 걸린 것은 removeNode입니다.

 

 

 괄호 부분을 보시면 됩니다. 그렇기 때문에, 기존에 k에 대응되는 value 값이 null이 아니였고, Bifunction이 null을 리턴하게 하면, (k, v)쌍이 제거가 됩니다. 이걸 이용해서 무엇을 할 수 있을까요?

 

 


 가장 간단하게 쓰일 수 있는 예로, multimap이나 Counter가 있을 텐데요. 해당 정수가 몇 번 나왔는지 저장하는 map을 만들어 보도록 하겠습니다. 먼저, counter는 어떤 수가 없었는데, 추가되면 1이 되어야 합니다. 있었는데 또 추가 되었다면, 횟수가 하나 증가해야 합니다. 그렇게 하려면, 추가를 할 때, 없다면 0으로 먼저 초기화를 하고, Bifunction을 적용시키면 될 겁니다.

 

 반면에, Integer가 하나 제거된다면, 횟수가 하나 감소해야 합니다. 그런데, 감소한 후에 빈도가 0이 되었다면, 제거가 되어야 합니다. 그렇다면, 제거하기 이전에 value 값이 1 이하였다면, 적용 함수가 null을 리턴하게 하면 됩니다. 이를 구현한 코드는 아래와 같습니다.

 

 

 추가할 때에는 (k,v) -> v+1을 적용했습니다. 그 전에, putIfabsent를 사용한 것도 보시면 됩니다. 그리고, 제거할 때 적용하는 함수는 삼항 연산자를 이용했는데요. 어렵지 않습니다. (k, v)쌍에서, v를 k가 나타난 횟수로 정의했기 때문에, 적용 이전에 v 값이 1보다 작거나 같았다면, null을 리턴하게끔 한 겁니다. 그러면, 적용 후에 apply가 된 값은 null이 되므로, 해당 key 값이 map에서 제거가 될 것이기 때문입니다.

 

 

 Main 클래스는 위와 같습니다. 먼저, 1이 2번 들어갔고, 2가 한 번 제거되었는데요. 2는 없었으므로 제거 연산은 무시됩니다. 그 다음에, 1을 제거하고, 2를 추가했으므로, 2번째까지 수행한 후에는 1이 1개, 2가 1개 있을 겁니다. 다음에, 1과 2를 제거하면, 1과 2는 없을 겁니다.

 

 위 프로그램의 실행 결과는 아래와 같습니다.

 

 

 의도한 대로 구현이 되었음을 알 수 있습니다.