All Downloads are FREE. Search and download functionalities are using the official Maven repository.

zio.concurrent.ConcurrentMap.scala Maven / Gradle / Ivy

package zio.concurrent

import zio.{Chunk, ChunkBuilder, UIO, ZIO}

import java.util.concurrent.ConcurrentHashMap

import java.util.function.BiConsumer
import scala.annotation.nowarn
import scala.collection.JavaConverters._

/**
 * Wrapper over `java.util.concurrent.ConcurrentHashMap`.
 */
final class ConcurrentMap[K, V] private (private val underlying: ConcurrentHashMap[K, V]) extends AnyVal {

  /**
   * Finds the first element of a map for which the partial function is defined
   * and applies the function to it.
   */
  def collectFirst[B](pf: PartialFunction[(K, V), B]): UIO[Option[B]] =
    ZIO.succeed {
      var result = Option.empty[B]
      underlying.forEach {
        makeBiConsumer { (k: K, v: V) =>
          if (result.isEmpty && pf.isDefinedAt((k, v)))
            result = Some(pf((k, v)))
        }
      }
      result
    }

  /**
   * Attempts to compute a mapping for the specified key and its current mapped
   * value (or None if there is no current mapping).
   */
  def compute(key: K, remap: (K, Option[V]) => Option[V]): UIO[Option[V]] =
    ZIO.succeed(Option(underlying.compute(key, remapComputeWith(remap))))

  /**
   * Computes a value of a non-existing key.
   */
  def computeIfAbsent(key: K, map: K => V): UIO[V] =
    ZIO.succeed(underlying.computeIfAbsent(key, mapWith(map)))

  /**
   * Attempts to compute a new mapping of an existing key.
   */

  def computeIfPresent(key: K, remap: (K, V) => V): UIO[Option[V]] =
    ZIO.succeed(Option(underlying.computeIfPresent(key, remapComputeIfPresentWith(remap))))

  /**
   * Tests whether a given predicate holds true for at least one element in a
   * map.
   */

  def exists(p: (K, V) => Boolean): UIO[Boolean] =
    ZIO.succeed {
      var result = false
      underlying.forEach {
        makeBiConsumer { (k: K, v: V) =>
          if (!result && p(k, v))
            result = true
        }
      }
      result
    }

  /**
   * Folds the elements of a map using the given binary operator.
   */
  def fold[S](zero: S)(f: (S, (K, V)) => S): UIO[S] =
    ZIO.succeed {
      var result: S = zero
      underlying.forEach {
        makeBiConsumer { (k: K, v: V) =>
          result = f(result, (k, v))
        }
      }
      result
    }

  /**
   * Tests whether a predicate is satisfied by all elements of a map.
   */
  def forall(p: (K, V) => Boolean): UIO[Boolean] =
    ZIO.succeed {
      var result = true
      underlying.forEach {
        makeBiConsumer { (k: K, v: V) =>
          if (result && !p(k, v))
            result = false
        }
      }
      result
    }

  /**
   * Retrieves the value associated with the given key.
   */
  def get(key: K): UIO[Option[V]] =
    ZIO.succeed(Option(underlying.get(key)))

  /**
   * Adds a new key-value pair and optionally returns previously bound value.
   */
  def put(key: K, value: V): UIO[Option[V]] =
    ZIO.succeed(Option(underlying.put(key, value)))

  /**
   * Adds all new key-value pairs
   */
  def putAll(keyValues: (K, V)*): UIO[Unit] =
    ZIO.succeed(underlying.putAll(keyValues.toMap.asJava): @nowarn("msg=JavaConverters"))

  /**
   * Adds a new key-value pair, unless the key is already bound to some other
   * value.
   */
  def putIfAbsent(key: K, value: V): UIO[Option[V]] =
    ZIO.succeed(Option(underlying.putIfAbsent(key, value)))

  /**
   * True if there are no elements in this map.
   */
  def isEmpty: UIO[Boolean] =
    ZIO.succeed(underlying.isEmpty)

  /**
   * Removes the entry for the given key, optionally returning value associated
   * with it.
   */
  def remove(key: K): UIO[Option[V]] =
    ZIO.succeed(Option(underlying.remove(key)))

  /**
   * Removes the entry for the given key if it is mapped to a given value.
   */
  def remove(key: K, value: V): UIO[Boolean] =
    ZIO.succeed(underlying.remove(key, value))

  /**
   * Removes all elements which do not satisfy the given predicate.
   */
  def removeIf(p: (K, V) => Boolean): UIO[Unit] =
    ZIO.succeed(
      underlying.forEach {
        makeBiConsumer { (k: K, v: V) =>
          if (p(k, v)) {
            val _ = underlying.remove(k)
          }
        }
      }
    )

  /**
   * Removes all elements which do not satisfy the given predicate.
   */
  def retainIf(p: (K, V) => Boolean): UIO[Unit] =
    ZIO.succeed(
      underlying.forEach {
        makeBiConsumer { (k: K, v: V) =>
          if (!p(k, v)) {
            val _ = underlying.remove(k)
          }
        }
      }
    )

  /**
   * Removes all elements.
   */
  def clear: UIO[Unit] =
    ZIO.succeed(underlying.clear())

  /**
   * Replaces the entry for the given key only if it is mapped to some value.
   */
  def replace(key: K, value: V): UIO[Option[V]] =
    ZIO.succeed(Option(underlying.replace(key, value)))

  /**
   * Replaces the entry for the given key only if it was previously mapped to a
   * given value.
   */
  def replace(key: K, oldValue: V, newValue: V): UIO[Boolean] =
    ZIO.succeed(underlying.replace(key, oldValue, newValue))

  /**
   * Collects all entries into a chunk.
   */
  def toChunk: UIO[Chunk[(K, V)]] =
    ZIO.succeed {
      val builder = ChunkBuilder.make[(K, V)]()

      val it = underlying.entrySet().iterator()
      while (it.hasNext()) {
        val entry = it.next()
        builder += entry.getKey() -> entry.getValue()
      }

      builder.result()
    }

  /**
   * Collects all entries into a list.
   */
  def toList: UIO[List[(K, V)]] =
    toChunk.map(_.toList)

  private[this] def mapWith(map: K => V): java.util.function.Function[K, V] =
    new java.util.function.Function[K, V] {
      def apply(k: K): V = map(k)
    }

  private[this] def remapComputeWith(remap: (K, Option[V]) => Option[V]): java.util.function.BiFunction[K, V, V] =
    new java.util.function.BiFunction[K, V, V] {
      def apply(k: K, v: V): V = remap(k, Option(v)).getOrElse(null.asInstanceOf[V])
    }

  private[this] def remapComputeIfPresentWith(remap: (K, V) => V): java.util.function.BiFunction[K, V, V] =
    new java.util.function.BiFunction[K, V, V] {
      def apply(k: K, v: V): V = if (v == null) v else remap(k, v)
    }

  private[this] def makeBiConsumer(f: (K, V) => Unit): BiConsumer[K, V] =
    new BiConsumer[K, V] {
      override def accept(t: K, u: V): Unit = f(t, u)
    }
}

object ConcurrentMap {

  /**
   * Makes an empty `ConcurrentMap`.
   */
  def empty[K, V]: UIO[ConcurrentMap[K, V]] =
    ZIO.succeed(new ConcurrentMap(new ConcurrentHashMap()))

  /**
   * Makes a new `ConcurrentMap` initialized with provided collection of
   * key-value pairs.
   */
  def fromIterable[K, V](pairs: Iterable[(K, V)]): UIO[ConcurrentMap[K, V]] =
    ZIO.succeed {
      val underlying = new ConcurrentHashMap[K, V]()

      pairs.foreach(kv => underlying.put(kv._1, kv._2))

      new ConcurrentMap(underlying)
    }

  /**
   * Makes a new `ConcurrentMap` initialized with provided key-value pairs.
   */
  def make[K, V](pairs: (K, V)*): UIO[ConcurrentMap[K, V]] =
    ZIO.succeed {
      val underlying = new ConcurrentHashMap[K, V]()

      pairs.foreach(kv => underlying.put(kv._1, kv._2))

      new ConcurrentMap(underlying)
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy