Skip to content

Commit

Permalink
implement ConcurrentMap wrapper around java ConcurrentHashMap
Browse files Browse the repository at this point in the history
Exactly like ConcurrentHashMap but using Option instead of null.
I don't like the scala.collection.Concurrent.Map overrides in
https://github.com/scala/scala/blob/2.13.x/src/library/scala/collection/convert/JavaCollectionWrappers.scala
e.g. the extra `underlying.containsKey(k)` in `get(k)`
and the absence of `computeIfPresent`.
  • Loading branch information
ornicar committed Jan 6, 2025
1 parent cffb4c7 commit ddedfb1
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 1 deletion.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ inThisBuild(
Seq(
scalaVersion := "3.6.2",
versionScheme := Some("early-semver"),
version := "11.5.4",
version := "11.6.0",
organization := "org.lichess",
licenses += ("MIT" -> url("https://opensource.org/licenses/MIT")),
publishTo := Option(Resolver.file("file", new File(sys.props.getOrElse("publishTo", ""))))
Expand Down
43 changes: 43 additions & 0 deletions lila/src/main/scala/ConcurrentMap.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package scalalib

import scala.jdk.CollectionConverters.*

/* Exactly like ConcurrentHashMap but using Option instead of null.
* I didn't like the scala.collection.Concurrent.Map overrides in
* https://github.com/scala/scala/blob/2.13.x/src/library/scala/collection/convert/JavaCollectionWrappers.scala
* e.g. the extra `underlying.containsKey(k)` in `get(k)`
* and the absence of `computeIfPresent`.
**/
final class ConcurrentMap[K, V](initialCapacity: Int):

private val underlying = new java.util.concurrent.ConcurrentHashMap[K, V](initialCapacity)

export underlying.{ put, size, contains, remove, containsKey, forEach as foreach }

def get(key: K): Option[V] = Option(underlying.get(key))

/* Runs f exactly once and returns the new value */
def compute(key: K)(f: Option[V] => Option[V]): Option[V] =
Option:
underlying.compute(key, (k, v) => f(Option(v)).getOrElse(null.asInstanceOf[V]))

/* Runs f at most once and returns the new value */
def computeIfPresent(key: K)(f: V => Option[V]): Option[V] =
Option:
underlying.computeIfPresent(key, (k, v) => f(v).getOrElse(null.asInstanceOf[V]))

/* Runs f at most once and returns the new value */
def computeIfAbsent(key: K)(f: => Option[V]): Option[V] =
Option:
underlying.computeIfAbsent(key, _ => f.getOrElse(null.asInstanceOf[V]))

/* Runs f at most once and returns the new value */
def computeIfAbsentAlways(key: K)(f: => V): V =
underlying.computeIfAbsent(key, _ => f)

def remove(key: K): Option[V] = Option(underlying.remove(key))

/* better than ConcurrentHashMap.getOrDefault because our default is lazy */
def getOrDefault(key: K, default: => V): V = get(key).getOrElse(default)

def keySet: Set[K] = underlying.keySet.asScala.toSet

0 comments on commit ddedfb1

Please sign in to comment.