/* TODO: Reintegrate /* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2008-2009, David MacIver ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ // $Id: $ package scala.collection.immutable object TreeHashMap { private[TreeHashMap] val _empty = new TreeHashMap[Any, Nothing](IntMap.empty[Nothing]); def empty[Key, Value] : TreeHashMap[Key, Value] = _empty.asInstanceOf[TreeHashMap[Key, Value]]; def apply[Key, Value](elems : (Key, Value)*) : TreeHashMap[Key, Value] = fromIterable(elems) def fromIterable[Key, Value](elems : Iterable[(Key, Value)]) : TreeHashMap[Key, Value] = elems.foldLeft(TreeHashMap.empty[Key, Value])((m, entry) => m.update(entry._1, entry._2)) private def apply[Key, Value](underlying : IntMap[AssocMap[Key, Value]]) = new TreeHashMap(underlying); } /** * An immutable Map which is based on an IntMap mapping hash codes to lists of (Key, Value) pairs with the * same hashCode. * * Unlike the usual immutable.HashMap it is truly immutable behind the scenes. Consequently it is entirely * safe to share between multiple threads and should achieve a much greater degree of sharing between instances. * * Performancewise, get and update seem to be somewhat slower than with a traditional hash map, but bulk operations * such as filter, foreach and transform appear to get something of a speedup. * * @author David MacIver */ class TreeHashMap[Key, +Value] private (private val underlying : IntMap[AssocMap[Key, Value]]) extends scala.collection.immutable.Map[Key, Value]{ def iterator : Iterator[(Key, Value)] = new Iterator[(Key, Value)]{ val assocIt = new AssocMapIterator(AssocMap.empty[Key, Value]); val intIterator = underlying.values; def hasNext = assocIt.hasNext || intIterator.hasNext; def next = { if (!assocIt.hasNext) assocIt.it = intIterator.next; assocIt.next } } def empty[V] = TreeHashMap.empty[Key, V] private def hash(key : Key) = { var h = key.hashCode; h ^= ((h >>> 20) ^ (h >>> 12)); h ^ (h >>> 7) ^ (h >>> 4); } override def stringPrefix = "TreeHashMap" override lazy val size = underlying.values.foldLeft(0)((x, y) => x + y.size); // todo: probably drop to make conform with general equals/hashcode policy override def equals(that : Any) = that match { case (that : TreeHashMap[_, _]) => if (this eq that) true else if (this.size != that.size) false; else this.underlying == that.underlying; case _ => false } // todo: probably drop to make conform with general equals/hashcode policy override def hashCode = underlying.hashCode override def foreach[U](f : ((Key, Value)) => U) = underlying.foreachValue(_.foreach(f)); override def toList : List[(Key, Value)] = { val buffer = new scala.collection.mutable.ListBuffer[(Key, Value)]; foreach(buffer += _); buffer.toList; } override def apply(key : Key) = underlying.get(hash(key)) match { case None => default(key); case Some(table) => table.getOrElse(key, default(key)); } override def isEmpty = underlying.isEmpty; override def filter(f : ((Key, Value)) => Boolean) : TreeHashMap[Key, Value]= { val newunderlying = underlying.modifyOrRemove( (key, x) => { val newtable = x.filter(f); if (newtable.isEmpty) None; else Some(newtable); } ) if (newunderlying eq underlying) this; else TreeHashMap(newunderlying); } override def transform[C](f : (Key, Value) => C) = { val newunderlying = underlying.transform( (key, value) => value.transform(f) ) if (underlying eq newunderlying) this.asInstanceOf[TreeHashMap[Key, C]]; else TreeHashMap(newunderlying); } def get(key : Key) = underlying.get(hash(key)).flatMap(_.get(key)); def update[S >: Value](key : Key, value : S) : TreeHashMap[Key, S] = TreeHashMap( underlying.updateWith[AssocMap[Key, S]](hash(key), AssocMap.singleton[Key, S](key, value), (x, y) => y.merge(x)) ) def -(key : Key) : TreeHashMap[Key, Value] = { val h = hash(key); underlying.get(h) match { case None => this; case Some(table) => { val newtable = table - key; if (table eq newtable) this; else if (newtable.isEmpty) TreeHashMap(underlying - h) else TreeHashMap(underlying.update(h, newtable)); } } } override def ++[V >: Value](that : Iterable[(Key, V)]) : TreeHashMap[Key, V] = that match { case (that : TreeHashMap[_, _]) => this ++ TreeHashMap.fromIterable(that.asInstanceOf[TreeHashMap[Key, V]]) case that => that.foldLeft(this : TreeHashMap[Key, V])((m, e) => m.update(e._1, e._2)); } def ++[V >: Value](that : TreeHashMap[Key, V]) : TreeHashMap[Key, V] = TreeHashMap(this.underlying.unionWith[AssocMap[Key, V]](that.underlying, (key, x, y) => x ++ y)); } private [collection] object AssocMap { val _empty = Nil[Any] def empty[Key, Value] : AssocMap[Key, Value] = _empty.asInstanceOf[AssocMap[Key, Value]] def singleton[Key, Value](key : Key, value : Value) = Cons(key, value, empty); def apply[Key, Value](maps : (Key, Value)*) = maps.foldLeft(empty[Key, Value])((x, y) => x.update(y._1, y._2)); private[collection] case class Nil[Key]() extends AssocMap[Key, Nothing] private[collection] case class Cons[S, +T](key: S, value: T, tail: AssocMap[S, T]) extends AssocMap[S, T] } import AssocMap._ // AssocMap is very similar to ListMap. I don't want to patch ListMap right // now, so I've got a separate implementation here to make tweaks to. Long // term any of these changes should be merged into ListMap // Short term it doesn't really matter because there are almost no viable // use cases for ListMap compared to one of the alternatives. private[collection] sealed abstract class AssocMap[Key, +Value] extends immutable.Map[Key, Value]{ def empty[V] = AssocMap.empty[Key, V] final def get(key : Key) : Option[Value] = this match { case Nil() => None; case Cons(key2, value, tail) => if (key == key2) Some(value) else tail.get(key); } override final def getOrElse[V >: Value](key : Key, default : =>V) : V = this match { case Nil() => default; case Cons(key2, value, tail) => if (key == key2) value else tail.getOrElse(key, default); } override final def apply(key : Key) : Value = getOrElse(key, default(key)); override def filter(f : ((Key, Value)) => Boolean) : AssocMap[Key, Value] = this match { case Cons(key, value, tail) => { val filteredtail = tail.filter(f); if (f((key, value))) { if (tail eq filteredtail) this; else Cons(key, value, filteredtail); } else filteredtail; } case Nil() => AssocMap.empty; } def iterator : Iterator[(Key, Value)] = new AssocMapIterator(this) @deprecated("use `iterator' instead") def elements = iterator override final def foreach[U](f : ((Key, Value)) => U) = this match { case Cons(key, value, tail) => { f((key, value)); tail.foreach(f); } case Nil() => {} } def size = this match { case Nil() => 0; case Cons(_, _, tail) => 1 + tail.size; } override def isEmpty = this match { case Nil() => true; case _ => false; } private def removeKeyValue(key : Any, value : Any) : Option[AssocMap[Key, Value]] = this match { case Nil() => None; case Cons(key2, value2, tail) => if (key == key2){ if (value == value2) Some(tail); else None; } else tail.removeKeyValue(key, value) match { case None => None; case Some(x) => Some(Cons(key2, value2, x)); } } private def sameMappings(that : AssocMap[_, _]) : Boolean = (this, that) match { case (Nil(), Nil()) => true; case (Nil(), _) => false; case (_, Nil()) => false; case (Cons(key, value, tail), that) => that.removeKeyValue(key, value) match { case None => false; case Some(x) => tail.sameMappings(x); } } override def equals(that : Any) = if (this eq that.asInstanceOf[AnyRef]) true; else that match { case (that : AssocMap[_, _]) => if (this.size != that.size) false; else this.sameMappings(that); case _ => false; } final def merge[S >: Value](that : AssocMap[Key, S]) : AssocMap[Key, S] = (this, that) match { case (Nil(), that) => that; case (_, Nil()) => this; case (Cons(key, value, tail), that) => tail.merge[S](that).update(key, value); } def update[S >: Value](key : Key, value : S) : AssocMap[Key, S] = this match { case Nil() => Cons(key, value, empty); case Cons(key2, value2, tail) => if (key2 == key) Cons(key, value, tail) else Cons(key2, value2, tail.update(key, value)); } override def transform[C](f: (Key, Value) => C): AssocMap[Key, C] = this match { case Nil() => AssocMap.empty[Key, C] case Cons(key, value, tail) => val newtail = tail.transform(f) val newval = f(key, value) if ((tail eq newtail) && (value.asInstanceOf[AnyRef] eq newval.asInstanceOf[AnyRef])) this.asInstanceOf[AssocMap[Key, C]]; else Cons(key, newval, newtail); } def -(key : Key) : AssocMap[Key, Value]= this match { case Nil() => this; case Cons(key2, value, tail) => if (key == key2) tail; else { val newtail = tail - key; if (tail eq newtail) this; else Cons(key2, value, newtail); } } final def ++[V >: Value](that : AssocMap[Key, V]) : AssocMap[Key, V] = (this, that) match { case (Nil(), that) => that; case (_, Nil()) => this; case (t1, Cons(key, value, tail)) => t1.update(key, value.asInstanceOf[Value] /* evil hack to work around bug 1140 */) ++ tail; } } private[collection] class AssocMapIterator[Key, Value](var it : AssocMap[Key, Value]) extends Iterator[(Key, Value)]{ def hasNext = it match { case Nil() => false; case _ => true; } def next = { val Cons(key, value, next) = it; it = next; (key, value); } } */