Coverage Summary for Class: HashMapVector (lzwpack.data)

Class Method, % Line, %
HashMapVector 88.9% (16/ 18) 90% (18/ 20)
HashMapVector$ 100% (3/ 3) 100% (4/ 4)
HashMapVector$mcB$sp 0% (0/ 11) 0% (0/ 13)
HashMapVector$mcC$sp 0% (0/ 11) 0% (0/ 13)
HashMapVector$mcD$sp 0% (0/ 11) 0% (0/ 13)
HashMapVector$mcF$sp 0% (0/ 11) 0% (0/ 13)
HashMapVector$mcI$sp 0% (0/ 11) 0% (0/ 13)
HashMapVector$mcJ$sp 0% (0/ 11) 0% (0/ 13)
HashMapVector$mcS$sp 0% (0/ 11) 0% (0/ 13)
HashMapVector$mcV$sp 0% (0/ 11) 0% (0/ 13)
HashMapVector$mcZ$sp 0% (0/ 11) 0% (0/ 13)
total 15.8% (19/ 120) 15.6% (22/ 141)


1 package lzwpack.data 2  3 import cats.{Eval, Now, Hash} 4 import cats.implicits._ 5  6 /** 7  * A [[HashMapVector]] is a hash map implementation on top of a bit-indexed trie. 8  * As such, it provides the same functionality and performance characteristics as 9  * [[SparseVector]], but instead of an integer key we can use any JVM object with 10  * `hashCode` implemented as the key. 11  */ 12 class HashMapVector[K: Hash, @specialized V] private[data](private val vector: SparseVector[HashMapVector[K, V]#Bucket]) { 13  // Use overflow lists ("buckets") to handle hashCode collisions 14  type Bucket = ListVector[(K, V)] 15  16  private def bucketForKey(key: K): Bucket = vector.get(key.hash).getOrElse(ListVector.empty) 17  18  // Sets (k -> v) in the given bucket 19  private def addOrReplaceInBucket(bucket: Bucket)(kv: (K, V)): Bucket = kv match { 20  case (key, _) => bucket.filter(_._1 =!= key) + kv 21  } 22  23  /** 24  * Returns a value for a given key. 25  * 26  * @throws scala.NoSuchElementException if there's no mapping from the given key to a value 27  */ 28  def apply(key: K): V = get(key).getOrElse(throw new NoSuchElementException) 29  30  /** 31  * Returns whether this HashMapVector contains a value for a given key. 32  */ 33  def contains(key: K): Boolean = get(key).nonEmpty 34  35  /** 36  * Returns an [[scala.Some]] with the value for the given key, or [[scala.None]] if there's no value. 37  */ 38  def get(key: K): Option[V] = for { 39  (_, value) <- bucketForKey(key).find(_._1 === key) 40  } yield value 41  42  /** 43  * Returns a new HashMapVector with the given key-value mapping changed. 44  */ 45  def updated(kv: (K, V)): HashMapVector[K, V] = 46  new HashMapVector(vector.updated(kv._1.hash, addOrReplaceInBucket(bucketForKey(kv._1))(kv))) 47  48  /** 49  * Alias for [[updated]] 50  */ 51  def +(kv: (K, V)): HashMapVector[K, V] = updated(kv) 52  53  /** 54  * Returns the number of key-value mappings in this HashMapVector. 55  */ 56  lazy val size: Int = vector.fold(0) { case ((_, xs), acc) => xs.size + acc } 57  58  /** 59  * Returns true if this HashMapVector contains no elements, false otherwise. 60  */ 61  def isEmpty: Boolean = vector.isEmpty 62  63  /** 64  * Folds the values of this HashMapVector with the given initial value and function. 65  * The order for traversing the values is unspecified. 66  */ 67  def fold[B](init: => B)(f: ((K, V), => B) => B): B = 68  vector.fold(init) { case ((_, kvs), rest) => kvs.foldRight(Eval.later(rest))((h, t) => Now(f(h, t.value))).value } 69  70  /** 71  * Returns the first element that `f` returns true for. If no element matches `f` then [[scala.None]] is returned. 72  * The order for traversing the values is unspecified. 73  */ 74  def find(f: (K, V) => Boolean): Option[(K, V)] = fold(Option.empty[(K, V)]) { 75  case ((k, v), acc) if f(k, v) => Some((k, v)) 76  case (_, acc) => acc 77  } 78  79  override def toString: String = s"HashMapVector($size elements)" 80 } 81  82 object HashMapVector { 83  /** 84  * Given a sequence of key-value tuples, constructs a new HashMapVector from them. 85  */ 86  def apply[K: Hash, V](kvs: (K, V)*): HashMapVector[K, V] = { 87  kvs.foldLeft(empty[K, V])((map, kv) => map + kv) 88  } 89  90  /** 91  * Returns an empty HashMapVector. 92  */ 93  def empty[K: Hash, V] = new HashMapVector[K, V](SparseVector.empty) 94 }