Skip to content

Commit 742a7fc

Browse files
committed
Add WeakHashMap and MultiMap to stdlib
1 parent a6d33ae commit 742a7fc

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Scala (https://www.scala-lang.org)
3+
*
4+
* Copyright EPFL and Lightbend, Inc.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (http://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package scala.collection.mutable
14+
15+
import language.experimental.captureChecking
16+
17+
/** A trait for mutable maps with multiple values assigned to a key.
18+
*
19+
* This class is typically used as a mixin. It turns maps which map `K`
20+
* to `Set[V]` objects into multimaps that map `K` to `V` objects.
21+
*
22+
* @example {{{
23+
* // first import all necessary types from package `collection.mutable`
24+
* import collection.mutable.{ HashMap, MultiMap, Set }
25+
*
26+
* // to create a `MultiMap` the easiest way is to mixin it into a normal
27+
* // `Map` instance
28+
* val mm = new HashMap[Int, Set[String]] with MultiMap[Int, String]
29+
*
30+
* // to add key-value pairs to a multimap it is important to use
31+
* // the method `addBinding` because standard methods like `+` will
32+
* // overwrite the complete key-value pair instead of adding the
33+
* // value to the existing key
34+
* mm.addBinding(1, "a")
35+
* mm.addBinding(2, "b")
36+
* mm.addBinding(1, "c")
37+
*
38+
* // mm now contains `Map(2 -> Set(b), 1 -> Set(c, a))`
39+
*
40+
* // to check if the multimap contains a value there is method
41+
* // `entryExists`, which allows to traverse the including set
42+
* mm.entryExists(1, _ == "a") == true
43+
* mm.entryExists(1, _ == "b") == false
44+
* mm.entryExists(2, _ == "b") == true
45+
*
46+
* // to remove a previous added value there is the method `removeBinding`
47+
* mm.removeBinding(1, "a")
48+
* mm.entryExists(1, _ == "a") == false
49+
* }}}
50+
*
51+
* @define coll multimap
52+
* @define Coll `MultiMap`
53+
*/
54+
@deprecated("Use a scala.collection.mutable.MultiDict in the scala-collection-contrib module", "2.13.0")
55+
trait MultiMap[K, V] extends Map[K, Set[V]] {
56+
/** Creates a new set.
57+
*
58+
* Classes that use this trait as a mixin can override this method
59+
* to have the desired implementation of sets assigned to new keys.
60+
* By default this is `HashSet`.
61+
*
62+
* @return An empty set of values of type `V`.
63+
*/
64+
protected def makeSet: Set[V] = new HashSet[V]
65+
66+
/** Assigns the specified `value` to a specified `key`. If the key
67+
* already has a binding to equal to `value`, nothing is changed;
68+
* otherwise a new binding is added for that `key`.
69+
*
70+
* @param key The key to which to bind the new value.
71+
* @param value The value to bind to the key.
72+
* @return A reference to this multimap.
73+
*/
74+
def addBinding(key: K, value: V): this.type = {
75+
get(key) match {
76+
case None =>
77+
val set = makeSet
78+
set += value
79+
this(key) = set
80+
case Some(set) =>
81+
set += value
82+
}
83+
this
84+
}
85+
86+
/** Removes the binding of `value` to `key` if it exists, otherwise this
87+
* operation doesn't have any effect.
88+
*
89+
* If this was the last value assigned to the specified key, the
90+
* set assigned to that key will be removed as well.
91+
*
92+
* @param key The key of the binding.
93+
* @param value The value to remove.
94+
* @return A reference to this multimap.
95+
*/
96+
def removeBinding(key: K, value: V): this.type = {
97+
get(key) match {
98+
case None =>
99+
case Some(set) =>
100+
set -= value
101+
if (set.isEmpty) this -= key
102+
}
103+
this
104+
}
105+
106+
/** Checks if there exists a binding to `key` such that it satisfies the predicate `p`.
107+
*
108+
* @param key The key for which the predicate is checked.
109+
* @param p The predicate which a value assigned to the key must satisfy.
110+
* @return A boolean if such a binding exists
111+
*/
112+
def entryExists(key: K, p: V => Boolean): Boolean = get(key) match {
113+
case None => false
114+
case Some(set) => set exists p
115+
}
116+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Scala (https://www.scala-lang.org)
3+
*
4+
* Copyright EPFL and Lightbend, Inc.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (http://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package scala
14+
package collection
15+
package mutable
16+
17+
import scala.annotation.nowarn
18+
import scala.collection.convert.JavaCollectionWrappers.{JMapWrapper, JMapWrapperLike}
19+
import language.experimental.captureChecking
20+
21+
/** A hash map with references to entries which are weakly reachable. Entries are
22+
* removed from this map when the key is no longer (strongly) referenced. This class wraps
23+
* `java.util.WeakHashMap`.
24+
*
25+
* @tparam K type of keys contained in this map
26+
* @tparam V type of values associated with the keys
27+
*
28+
* @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#weak-hash-maps "Scala's Collection Library overview"]]
29+
* section on `Weak Hash Maps` for more information.
30+
*
31+
* @define Coll `WeakHashMap`
32+
* @define coll weak hash map
33+
* @define mayNotTerminateInf
34+
* @define willNotTerminateInf
35+
*/
36+
@SerialVersionUID(3L)
37+
class WeakHashMap[sealed K, sealed V] extends JMapWrapper[K, V](new java.util.WeakHashMap)
38+
with JMapWrapperLike[K, V, WeakHashMap, WeakHashMap[K, V]]
39+
with MapFactoryDefaults[K, V, WeakHashMap, Iterable] {
40+
override def empty = new WeakHashMap[K, V]
41+
override def mapFactory: MapFactory[WeakHashMap] = WeakHashMap
42+
@nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""")
43+
override protected[this] def stringPrefix = "WeakHashMap"
44+
}
45+
46+
/** $factoryInfo
47+
* @define Coll `WeakHashMap`
48+
* @define coll weak hash map
49+
*/
50+
@SerialVersionUID(3L)
51+
object WeakHashMap extends MapFactory[WeakHashMap] {
52+
def empty[sealed K, sealed V]: WeakHashMap[K,V] = new WeakHashMap[K, V]
53+
def from[sealed K, sealed V](it: collection.IterableOnce[(K, V)]^): WeakHashMap[K,V] = Growable.from(empty[K, V], it)
54+
def newBuilder[sealed K, sealed V]: Builder[(K, V), WeakHashMap[K,V]] = new GrowableBuilder(WeakHashMap.empty[K, V])
55+
}
56+

0 commit comments

Comments
 (0)