Francis
Francis

Reputation: 379

How to use ConcurrentHashMap computeIfAbsent() in Scala

I'm using a ConcurrentHashMap in Scala and I would like to use the computeIfAbsent() method but can't figure out the syntax for the second argument. Can someone show me what would be the proper syntax?

When running the following code

val data = new ConcurrentHashMap[String, LongAdder]

data.computeIfAbsent("bob", k: String => new LongAdder()).increment()

I'm getting the following error

Type mismatch, expected: Function[_ >: String, _ <: LongAdder], actual: (String) => Any

Thanking you in advance

Francis

Upvotes: 6

Views: 4866

Answers (2)

Mifeet
Mifeet

Reputation: 13648

The problem is that you're using java.util.concurrent.ConcurrentHashMap, which accepts java.util.function.Function as a parameter for computeIfAbsent() instead of scala.Function1 which you pass to it.

Since scala doesn't support lambda conversion for functional interfaces as Java does (at least not without the -Xexperimental flag), you can solve this by implementing a java.util.function.Function explicitly:

val data = new ConcurrentHashMap[String, LongAdder]
val adderSupplier = new java.util.function.Function[String, LongAdder]() {
  override def apply(t: String): LongAdder = new LongAdder()
}
data.computeIfAbsent("bob", adderSupplier).increment()

Alternatively, if you need this more often, you may write a utility conversion function or even an implicit conversion:

object FunctionConverter {
  implicit def scalaFunctionToJava[From, To](function: (From) => To): java.util.function.Function[From, To] = {
    new java.util.function.Function[From, To] {
      override def apply(input: From): To = function(input)
    }
  }
}

import FunctionConverter._
val data = new ConcurrentHashMap[String, LongAdder]()
data.computeIfAbsent("bob", (k: String) => new LongAdder()) // <- implicit conversion applied here

Upvotes: 10

Łukasz
Łukasz

Reputation: 8673

If you enable -Xexperimental flag you can use scala anonymous function notation for this:

scala> val data = new java.util.concurrent.ConcurrentHashMap[String, Int]
data: java.util.concurrent.ConcurrentHashMap[String,Int] = {}

scala> data.computeIfAbsent("bob", _.size)
res0: Int = 3

Note that you still can't pass regular scala Function

scala> val f: String => Int = _.size
f: String => Int = <function1>

scala> data.computeIfAbsent("bob", f)
<console>:13: error: type mismatch;
 found   : String => Int
 required: java.util.function.Function[_ >: String, _ <: Int]
       data.computeIfAbsent("bob", f)
                               ^

But eta-expansion will work

scala> def a(s: String): Int = s.size
a: (s: String)Int

scala> data.computeIfAbsent("bob", a)
res3: Int = 3

Upvotes: 2

Related Questions