Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions core/src/main/scala/cats/collections/HashMap.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package cats.collections

import cats._
import cats.implicits._

class HashMap[K, V](val buckets: Vector[List[(K, V)]]) {

def +(pair: (K, V))(implicit K: Hash[K]): HashMap[K, V] = {
def updateBucket(bucket: List[(K, V)], key: K, value: V): List[(K, V)] =
bucket match {
case Nil => (key, value) :: Nil
case (bk, _) :: tail if bk === key => (key, value) :: tail
case head :: tail => head :: updateBucket(tail, key, value)
}

val (key, value) = pair
val keyBucketIndex = key.hash % buckets.size
val keyBucket = buckets(keyBucketIndex)
val updatedKeyBucket = updateBucket(keyBucket, key, value)
new HashMap(buckets.updated(keyBucketIndex, updatedKeyBucket))
}

def get(key: K)(implicit K: Eq[K]): Option[V] = if(buckets.nonEmpty) {
val bucketIndex = key.hashCode % buckets.size
buckets(bucketIndex).collectFirst {
case (pkey, pvalue) if pkey === key => pvalue
}
} else None

def entryHashSet(implicit K: Hash[K], V: Hash[V]): HashSet[(K, V)] = buckets.foldLeft(HashSet.empty[(K, V)]) {
case (oset, bucket) => bucket.foldLeft(oset)(_ + _)
}

}

object HashMap extends HashMapInstances {

val defaultCapacity = 16

def apply[K: Hash, V](entries: (K, V)*): HashMap[K, V] = entries.foldLeft(empty[K, V])(_ + _)

def empty[K, V]: HashMap[K, V] = new HashMap[K, V](Vector.fill(defaultCapacity)(List.empty))

}

trait HashMapInstances {

implicit def eqMap[K, V](implicit K: Hash[K], V: Hash[V]): Eq[HashMap[K, V]] = new Eq[HashMap[K, V]] {

override def eqv(x: HashMap[K,V], y: HashMap[K,V]): Boolean = {
x.entryHashSet === y.entryHashSet
}

}

}
48 changes: 48 additions & 0 deletions core/src/main/scala/cats/collections/HashSet.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package cats.collections

import cats._
import cats.implicits._

class HashSet[A](val buckets: Vector[List[A]]) {

def +(a: A)(implicit hash: Hash[A]): HashSet[A] = {
def updateBucket(bucket: List[A], value: A): List[A] =
bucket match {
case Nil => a :: Nil
case h :: tail =>
if(h === value) h :: tail
else h :: updateBucket(tail, value)
}

val bucketIndex = a.hash % buckets.size
val bucket = buckets(bucketIndex)
val updatedBucket = updateBucket(bucket, a)
new HashSet(buckets.updated(bucketIndex, updatedBucket))
}

def toIterator: Iterator[A] =
buckets.flatten.toIterator

}

object HashSet extends HashSetInstances {

val defaultCapacity = 16

def apply[A: Hash](as: A*): HashSet[A] =
as.foldLeft(empty[A])(_ + _)

def empty[A]: HashSet[A] = new HashSet(Vector.fill(defaultCapacity)(List.empty))

}

trait HashSetInstances {

implicit def eqSet[A](implicit A: Eq[A]): Eq[HashSet[A]] = new Eq[HashSet[A]] {

override def eqv(x: HashSet[A], y: HashSet[A]): Boolean =
iteratorEq(x.toIterator, y.toIterator)

}

}