/
Map.scala
420 lines (368 loc) · 17.7 KB
/
Map.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package scala
package collection
import scala.annotation.nowarn
import scala.collection.generic.DefaultSerializable
import scala.collection.mutable.StringBuilder
import scala.util.hashing.MurmurHash3
/** Base Map type */
trait Map[K, +V]
extends Iterable[(K, V)]
with MapOps[K, V, Map, Map[K, V]]
with MapFactoryDefaults[K, V, Map, Iterable]
with Equals {
def mapFactory: scala.collection.MapFactory[Map] = Map
def canEqual(that: Any): Boolean = true
/**
* Equality of maps is implemented using the lookup method [[get]]. This method returns `true` if
* - the argument `o` is a `Map`,
* - the two maps have the same [[size]], and
* - for every `(key, value)` pair in this map, `other.get(key) == Some(value)`.
*
* The implementation of `equals` checks the [[canEqual]] method, so subclasses of `Map` can narrow down the equality
* to specific map types. The `Map` implementations in the standard library can all be compared, their `canEqual`
* methods return `true`.
*
* Note: The `equals` method only respects the equality laws (symmetry, transitivity) if the two maps use the same
* key equivalence function in their lookup operation. For example, the key equivalence operation in a
* [[scala.collection.immutable.TreeMap]] is defined by its ordering. Comparing a `TreeMap` with a `HashMap` leads
* to unexpected results if `ordering.equiv(k1, k2)` (used for lookup in `TreeMap`) is different from `k1 == k2`
* (used for lookup in `HashMap`).
*
* {{{
* scala> import scala.collection.immutable._
* scala> val ord: Ordering[String] = _ compareToIgnoreCase _
*
* scala> TreeMap("A" -> 1)(ord) == HashMap("a" -> 1)
* val res0: Boolean = false
*
* scala> HashMap("a" -> 1) == TreeMap("A" -> 1)(ord)
* val res1: Boolean = true
* }}}
*
*
* @param o The map to which this map is compared
* @return `true` if the two maps are equal according to the description
*/
override def equals(o: Any): Boolean =
(this eq o.asInstanceOf[AnyRef]) || (o match {
case map: Map[K @unchecked, _] if map.canEqual(this) =>
(this.size == map.size) && {
try this.forall(kv => map.getOrElse(kv._1, Map.DefaultSentinelFn()) == kv._2)
catch { case _: ClassCastException => false } // PR #9565 / scala/bug#12228
}
case _ =>
false
})
override def hashCode(): Int = MurmurHash3.mapHash(this)
// These two methods are not in MapOps so that MapView is not forced to implement them
@deprecated("Use - or removed on an immutable Map", "2.13.0")
def - (key: K): Map[K, V]
@deprecated("Use -- or removedAll on an immutable Map", "2.13.0")
def - (key1: K, key2: K, keys: K*): Map[K, V]
@nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""")
override protected[this] def stringPrefix: String = "Map"
override def toString(): String = super[Iterable].toString() // Because `Function1` overrides `toString` too
}
/** Base Map implementation type
*
* @tparam K Type of keys
* @tparam V Type of values
* @tparam CC type constructor of the map (e.g. `HashMap`). Operations returning a collection
* with a different type of entries `(L, W)` (e.g. `map`) return a `CC[L, W]`.
* @tparam C type of the map (e.g. `HashMap[Int, String]`). Operations returning a collection
* with the same type of element (e.g. `drop`, `filter`) return a `C`.
* @define coll map
* @define Coll `Map`
*/
// Note: the upper bound constraint on CC is useful only to
// erase CC to IterableOps instead of Object
trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C]
extends IterableOps[(K, V), Iterable, C]
with PartialFunction[K, V] {
override def view: MapView[K, V] = new MapView.Id(this)
/** Returns a [[Stepper]] for the keys of this map. See method [[stepper]]. */
def keyStepper[S <: Stepper[_]](implicit shape: StepperShape[K, S]): S = {
import convert.impl._
val s = shape.shape match {
case StepperShape.IntShape => new IntIteratorStepper (keysIterator.asInstanceOf[Iterator[Int]])
case StepperShape.LongShape => new LongIteratorStepper (keysIterator.asInstanceOf[Iterator[Long]])
case StepperShape.DoubleShape => new DoubleIteratorStepper(keysIterator.asInstanceOf[Iterator[Double]])
case _ => shape.seqUnbox(new AnyIteratorStepper(keysIterator))
}
s.asInstanceOf[S]
}
/** Returns a [[Stepper]] for the values of this map. See method [[stepper]]. */
def valueStepper[S <: Stepper[_]](implicit shape: StepperShape[V, S]): S = {
import convert.impl._
val s = shape.shape match {
case StepperShape.IntShape => new IntIteratorStepper (valuesIterator.asInstanceOf[Iterator[Int]])
case StepperShape.LongShape => new LongIteratorStepper (valuesIterator.asInstanceOf[Iterator[Long]])
case StepperShape.DoubleShape => new DoubleIteratorStepper(valuesIterator.asInstanceOf[Iterator[Double]])
case _ => shape.seqUnbox(new AnyIteratorStepper(valuesIterator))
}
s.asInstanceOf[S]
}
/** Similar to `fromIterable`, but returns a Map collection type.
* Note that the return type is now `CC[K2, V2]`.
*/
@`inline` protected final def mapFromIterable[K2, V2](it: Iterable[(K2, V2)]): CC[K2, V2] = mapFactory.from(it)
/** The companion object of this map, providing various factory methods.
*
* @note When implementing a custom collection type and refining `CC` to the new type, this
* method needs to be overridden to return a factory for the new type (the compiler will
* issue an error otherwise).
*/
def mapFactory: MapFactory[CC]
/** Optionally returns the value associated with a key.
*
* @param key the key value
* @return an option value containing the value associated with `key` in this map,
* or `None` if none exists.
*/
def get(key: K): Option[V]
/** Returns the value associated with a key, or a default value if the key is not contained in the map.
* @param key the key.
* @param default a computation that yields a default value in case no binding for `key` is
* found in the map.
* @tparam V1 the result type of the default computation.
* @return the value associated with `key` if it exists,
* otherwise the result of the `default` computation.
*/
def getOrElse[V1 >: V](key: K, default: => V1): V1 = get(key) match {
case Some(v) => v
case None => default
}
/** Retrieves the value which is associated with the given key. This
* method invokes the `default` method of the map if there is no mapping
* from the given key to a value. Unless overridden, the `default` method throws a
* `NoSuchElementException`.
*
* @param key the key
* @return the value associated with the given key, or the result of the
* map's `default` method, if none exists.
*/
@throws[NoSuchElementException]
def apply(key: K): V = get(key) match {
case None => default(key)
case Some(value) => value
}
override /*PartialFunction*/ def applyOrElse[K1 <: K, V1 >: V](x: K1, default: K1 => V1): V1 = getOrElse(x, default(x))
/** A set representing the keys contained by this map.
*
* For efficiency the resulting set may be a view (maintaining a reference to the map and reflecting modifications
* to the map), but it may also be a strict collection without reference to the map.
*
* - To ensure an independent strict collection, use `m.keysIterator.toSet`
* - To obtain a view on the keys, use `scala.collection.View.fromIteratorProvider(m.keysIterator)`
*
* @return a set representing the keys contained by this map
*/
def keySet: Set[K] = new KeySet
/** The implementation class of the set returned by `keySet`.
*/
protected class KeySet extends AbstractSet[K] with GenKeySet with DefaultSerializable {
def diff(that: Set[K]): Set[K] = fromSpecific(this.view.filterNot(that))
}
/** A generic trait that is reused by keyset implementations */
protected trait GenKeySet { this: Set[K] =>
def iterator: Iterator[K] = MapOps.this.keysIterator
def contains(key: K): Boolean = MapOps.this.contains(key)
override def size: Int = MapOps.this.size
override def knownSize: Int = MapOps.this.knownSize
override def isEmpty: Boolean = MapOps.this.isEmpty
}
/** An [[Iterable]] collection of the keys contained by this map.
*
* For efficiency the resulting collection may be a view (maintaining a reference to the map and reflecting
* modifications to the map), but it may also be a strict collection without reference to the map.
*
* - To ensure an independent strict collection, use `m.keysIterator.toSet`
* - To obtain a view on the keys, use `scala.collection.View.fromIteratorProvider(m.keysIterator)`
*
* @return an [[Iterable]] collection of the keys contained by this map
*/
@deprecatedOverriding("This method should be an alias for keySet", since="2.13.13")
def keys: Iterable[K] = keySet
/** Collects all values of this map in an iterable collection.
*
* @return the values of this map as an iterable.
*/
def values: Iterable[V] = new AbstractIterable[V] with DefaultSerializable {
override def knownSize: Int = MapOps.this.knownSize
override def iterator: Iterator[V] = valuesIterator
}
/** An [[Iterator]] of the keys contained by this map.
*
* @return an [[Iterator]] of the keys contained by this map
*/
def keysIterator: Iterator[K] = new AbstractIterator[K] {
val iter = MapOps.this.iterator
def hasNext = iter.hasNext
def next() = iter.next()._1
}
/** Creates an iterator for all values in this map.
*
* @return an iterator over all values that are associated with some key in this map.
*/
def valuesIterator: Iterator[V] = new AbstractIterator[V] {
val iter = MapOps.this.iterator
def hasNext = iter.hasNext
def next() = iter.next()._2
}
/** Apply `f` to each key/value pair for its side effects
* Note: [U] parameter needed to help scalac's type inference.
*/
def foreachEntry[U](f: (K, V) => U): Unit = {
val it = iterator
while (it.hasNext) {
val next = it.next()
f(next._1, next._2)
}
}
/** Filters this map by retaining only keys satisfying a predicate.
* @param p the predicate used to test keys
* @return an immutable map consisting only of those key value pairs of this map where the key satisfies
* the predicate `p`. The resulting map wraps the original map without copying any elements.
*/
@deprecated("Use .view.filterKeys(f). A future version will include a strict version of this method (for now, .view.filterKeys(p).toMap).", "2.13.0")
def filterKeys(p: K => Boolean): MapView[K, V] = new MapView.FilterKeys(this, p)
/** Transforms this map by applying a function to every retrieved value.
* @param f the function used to transform values of this map.
* @return a map view which maps every key of this map
* to `f(this(key))`. The resulting map wraps the original map without copying any elements.
*/
@deprecated("Use .view.mapValues(f). A future version will include a strict version of this method (for now, .view.mapValues(f).toMap).", "2.13.0")
def mapValues[W](f: V => W): MapView[K, W] = new MapView.MapValues(this, f)
/** Defines the default value computation for the map,
* returned when a key is not found.
*
* The method implemented here throws an exception,
* but it may be overridden by subclasses.
*
* @param key the given key value for which a binding is missing.
* @throws NoSuchElementException if no default value is defined
*/
@throws[NoSuchElementException]
def default(key: K): V =
throw new NoSuchElementException("key not found: " + key)
/** Tests whether this map contains a binding for a key.
*
* @param key the key
* @return `true` if there is a binding for `key` in this map, `false` otherwise.
*/
def contains(key: K): Boolean = get(key).isDefined
/** Tests whether this map contains a binding for a key. This method,
* which implements an abstract method of trait `PartialFunction`,
* is equivalent to `contains`.
*
* @param key the key
* @return `true` if there is a binding for `key` in this map, `false` otherwise.
*/
def isDefinedAt(key: K): Boolean = contains(key)
/** Builds a new map by applying a function to all elements of this $coll.
*
* @param f the function to apply to each element.
* @return a new $coll resulting from applying the given function
* `f` to each element of this $coll and collecting the results.
*/
def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] = mapFactory.from(new View.Map(this, f))
/** Builds a new collection by applying a partial function to all elements of this $coll
* on which the function is defined.
*
* @param pf the partial function which filters and maps the $coll.
* @tparam K2 the key type of the returned $coll.
* @tparam V2 the value type of the returned $coll.
* @return a new $coll resulting from applying the given partial function
* `pf` to each element on which it is defined and collecting the results.
* The order of the elements is preserved.
*/
def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]): CC[K2, V2] =
mapFactory.from(new View.Collect(this, pf))
/** Builds a new map by applying a function to all elements of this $coll
* and using the elements of the resulting collections.
*
* @param f the function to apply to each element.
* @return a new $coll resulting from applying the given collection-valued function
* `f` to each element of this $coll and concatenating the results.
*/
def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): CC[K2, V2] = mapFactory.from(new View.FlatMap(this, f))
/** Returns a new $coll containing the elements from the left hand operand followed by the elements from the
* right hand operand. The element type of the $coll is the most specific superclass encompassing
* the element types of the two operands.
*
* @param suffix the iterable to append.
* @return a new $coll which contains all elements
* of this $coll followed by all elements of `suffix`.
*/
def concat[V2 >: V](suffix: collection.IterableOnce[(K, V2)]): CC[K, V2] = mapFactory.from(suffix match {
case it: Iterable[(K, V2)] => new View.Concat(this, it)
case _ => iterator.concat(suffix.iterator)
})
// Not final because subclasses refine the result type, e.g. in SortedMap, the result type is
// SortedMap's CC, while Map's CC is fixed to Map
/** Alias for `concat` */
/*@`inline` final*/ def ++ [V2 >: V](xs: collection.IterableOnce[(K, V2)]): CC[K, V2] = concat(xs)
override def addString(sb: StringBuilder, start: String, sep: String, end: String): sb.type =
iterator.map { case (k, v) => s"$k -> $v" }.addString(sb, start, sep, end)
@deprecated("Consider requiring an immutable Map or fall back to Map.concat.", "2.13.0")
def + [V1 >: V](kv: (K, V1)): CC[K, V1] =
mapFactory.from(new View.Appended(this, kv))
@deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0")
def + [V1 >: V](elem1: (K, V1), elem2: (K, V1), elems: (K, V1)*): CC[K, V1] =
mapFactory.from(new View.Concat(new View.Appended(new View.Appended(this, elem1), elem2), elems))
@deprecated("Consider requiring an immutable Map.", "2.13.0")
@`inline` def -- (keys: IterableOnce[K]): C = {
lazy val keysSet = keys.iterator.to(immutable.Set)
fromSpecific(this.view.filterKeys(k => !keysSet.contains(k)))
}
@deprecated("Use ++ instead of ++: for collections of type Iterable", "2.13.0")
def ++: [V1 >: V](that: IterableOnce[(K,V1)]): CC[K,V1] = {
val thatIterable: Iterable[(K, V1)] = that match {
case that: Iterable[(K, V1)] => that
case that => View.from(that)
}
mapFactory.from(new View.Concat(thatIterable, this))
}
}
object MapOps {
/** Specializes `WithFilter` for Map collection types by adding overloads to transformation
* operations that can return a Map.
*
* @define coll map collection
*/
@SerialVersionUID(3L)
class WithFilter[K, +V, +IterableCC[_], +CC[_, _] <: IterableOps[_, AnyConstr, _]](
self: MapOps[K, V, CC, _] with IterableOps[(K, V), IterableCC, _],
p: ((K, V)) => Boolean
) extends IterableOps.WithFilter[(K, V), IterableCC](self, p) with Serializable {
def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] =
self.mapFactory.from(new View.Map(filtered, f))
def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): CC[K2, V2] =
self.mapFactory.from(new View.FlatMap(filtered, f))
override def withFilter(q: ((K, V)) => Boolean): WithFilter[K, V, IterableCC, CC] =
new WithFilter[K, V, IterableCC, CC](self, (kv: (K, V)) => p(kv) && q(kv))
}
}
/**
* $factoryInfo
* @define coll map
* @define Coll `Map`
*/
@SerialVersionUID(3L)
object Map extends MapFactory.Delegate[Map](immutable.Map) {
private val DefaultSentinel: AnyRef = new AnyRef
private val DefaultSentinelFn: () => AnyRef = () => DefaultSentinel
}
/** Explicit instantiation of the `Map` trait to reduce class file size in subclasses. */
abstract class AbstractMap[K, +V] extends AbstractIterable[(K, V)] with Map[K, V]