Skip to content

Commit 26dde72

Browse files
committed
Add immutable SortedMap to stdlib
1 parent d526e10 commit 26dde72

File tree

1 file changed

+178
-0
lines changed

1 file changed

+178
-0
lines changed
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
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 immutable
16+
17+
import scala.annotation.unchecked.uncheckedVariance
18+
import scala.collection.mutable.Builder
19+
import language.experimental.captureChecking
20+
21+
/** An immutable map whose key-value pairs are sorted according to an [[scala.math.Ordering]] on the keys.
22+
*
23+
* Allows for range queries to be performed on its keys, and implementations must guarantee that traversal happens in
24+
* sorted order, according to the map's [[scala.math.Ordering]].
25+
*
26+
* @example {{{
27+
* import scala.collection.immutable.SortedMap
28+
*
29+
* // Make a SortedMap via the companion object factory
30+
* val weekdays = SortedMap(
31+
* 2 -> "Monday",
32+
* 3 -> "Tuesday",
33+
* 4 -> "Wednesday",
34+
* 5 -> "Thursday",
35+
* 6 -> "Friday"
36+
* )
37+
* // TreeMap(2 -> Monday, 3 -> Tuesday, 4 -> Wednesday, 5 -> Thursday, 6 -> Friday)
38+
*
39+
* val days = weekdays ++ List(1 -> "Sunday", 7 -> "Saturday")
40+
* // TreeMap(1 -> Sunday, 2 -> Monday, 3 -> Tuesday, 4 -> Wednesday, 5 -> Thursday, 6 -> Friday, 7 -> Saturday)
41+
*
42+
* val day3 = days.get(3) // Some("Tuesday")
43+
*
44+
* val rangeOfDays = days.range(2, 5) // TreeMap(2 -> Monday, 3 -> Tuesday, 4 -> Wednesday)
45+
*
46+
* val daysUntil2 = days.rangeUntil(2) // TreeMap(1 -> Sunday)
47+
* val daysTo2 = days.rangeTo(2) // TreeMap(1 -> Sunday, 2 -> Monday)
48+
* val daysAfter5 = days.rangeFrom(5) // TreeMap(5 -> Thursday, 6 -> Friday, 7 -> Saturday)
49+
* }}}
50+
*
51+
* @tparam K the type of the keys contained in this tree map.
52+
* @tparam V the type of the values associated with the keys.
53+
*/
54+
trait SortedMap[K, +V]
55+
extends Map[K, V]
56+
with collection.SortedMap[K, V]
57+
with SortedMapOps[K, V, SortedMap, SortedMap[K, V]]
58+
with SortedMapFactoryDefaults[K, V, SortedMap, Iterable, Map] {
59+
60+
override def unsorted: Map[K, V] = this
61+
62+
override def sortedMapFactory: SortedMapFactory[SortedMap] = SortedMap
63+
64+
/** The same map with a given default function.
65+
* Note: The default is only used for `apply`. Other methods like `get`, `contains`, `iterator`, `keys`, etc.
66+
* are not affected by `withDefault`.
67+
*
68+
* Invoking transformer methods (e.g. `map`) will not preserve the default value.
69+
*
70+
* @param d the function mapping keys to values, used for non-present keys
71+
* @return a wrapper of the map with a default value
72+
*/
73+
override def withDefault[V1 >: V](d: K -> V1): SortedMap[K, V1] = new SortedMap.WithDefault[K, V1](this, d)
74+
75+
/** The same map with a given default value.
76+
* Note: The default is only used for `apply`. Other methods like `get`, `contains`, `iterator`, `keys`, etc.
77+
* are not affected by `withDefaultValue`.
78+
*
79+
* Invoking transformer methods (e.g. `map`) will not preserve the default value.
80+
*
81+
* @param d default value used for non-present keys
82+
* @return a wrapper of the map with a default value
83+
*/
84+
override def withDefaultValue[V1 >: V](d: V1): SortedMap[K, V1] = new SortedMap.WithDefault[K, V1](this, _ => d)
85+
}
86+
87+
trait SortedMapOps[K, +V, +CC[X, +Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _], +C <: SortedMapOps[K, V, CC, C]]
88+
extends MapOps[K, V, Map, C] with collection.SortedMapOps[K, V, CC, C] { self =>
89+
90+
protected def coll: C with CC[K, V]
91+
92+
def unsorted: Map[K, V]
93+
94+
override def keySet: SortedSet[K] = new ImmutableKeySortedSet
95+
96+
/** The implementation class of the set returned by `keySet` */
97+
protected class ImmutableKeySortedSet extends AbstractSet[K] with SortedSet[K] with GenKeySet with GenKeySortedSet {
98+
def rangeImpl(from: Option[K], until: Option[K]): SortedSet[K] = {
99+
val map = self.rangeImpl(from, until)
100+
new map.ImmutableKeySortedSet
101+
}
102+
def incl(elem: K): SortedSet[K] = fromSpecific(this).incl(elem)
103+
def excl(elem: K): SortedSet[K] = fromSpecific(this).excl(elem)
104+
}
105+
106+
// We override these methods to fix their return type (which would be `Map` otherwise)
107+
def updated[V1 >: V](key: K, value: V1): CC[K, V1]
108+
@`inline` final override def +[V1 >: V](kv: (K, V1)): CC[K, V1] = updated(kv._1, kv._2)
109+
override def updatedWith[V1 >: V](key: K)(remappingFunction: Option[V] => Option[V1]): CC[K, V1] = {
110+
// Implementation has been copied from `MapOps`
111+
val previousValue = this.get(key)
112+
remappingFunction(previousValue) match {
113+
case None => previousValue.fold(coll)(_ => this.removed(key).coll)
114+
case Some(nextValue) =>
115+
if (previousValue.exists(_.asInstanceOf[AnyRef] eq nextValue.asInstanceOf[AnyRef])) coll
116+
else coll.updated(key, nextValue)
117+
}
118+
}
119+
override def transform[W](f: (K, V) => W): CC[K, W] = map({ case (k, v) => (k, f(k, v)) })(ordering)
120+
}
121+
122+
trait StrictOptimizedSortedMapOps[K, +V, +CC[X, +Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _], +C <: SortedMapOps[K, V, CC, C]]
123+
extends SortedMapOps[K, V, CC, C]
124+
with collection.StrictOptimizedSortedMapOps[K, V, CC, C]
125+
with StrictOptimizedMapOps[K, V, Map, C] {
126+
127+
override def concat[V2 >: V](xs: collection.IterableOnce[(K, V2)]^): CC[K, V2] = {
128+
var result: CC[K, V2] = coll
129+
val it = xs.iterator
130+
while (it.hasNext) result = result + it.next()
131+
result
132+
}
133+
}
134+
135+
@SerialVersionUID(3L)
136+
object SortedMap extends SortedMapFactory.Delegate[SortedMap](TreeMap) {
137+
138+
override def from[K: Ordering, V](it: IterableOnce[(K, V)]^): SortedMap[K, V] = it match {
139+
case sm: SortedMap[K, V] if Ordering[K] == sm.ordering => sm
140+
case _ => super.from(it)
141+
}
142+
143+
final class WithDefault[K, +V](underlying: SortedMap[K, V], defaultValue: K -> V)
144+
extends Map.WithDefault[K, V](underlying, defaultValue)
145+
with SortedMap[K, V]
146+
with SortedMapOps[K, V, SortedMap, WithDefault[K, V]] with Serializable {
147+
148+
implicit def ordering: Ordering[K] = underlying.ordering
149+
150+
override def sortedMapFactory: SortedMapFactory[SortedMap] = underlying.sortedMapFactory
151+
152+
def iteratorFrom(start: K): scala.collection.Iterator[(K, V)] = underlying.iteratorFrom(start)
153+
154+
def keysIteratorFrom(start: K): scala.collection.Iterator[K] = underlying.keysIteratorFrom(start)
155+
156+
def rangeImpl(from: Option[K], until: Option[K]): WithDefault[K, V] =
157+
new WithDefault[K, V](underlying.rangeImpl(from, until), defaultValue)
158+
159+
// Need to override following methods to match type signatures of `SortedMap.WithDefault`
160+
// for operations preserving default value
161+
162+
override def updated[V1 >: V](key: K, value: V1): WithDefault[K, V1] =
163+
new WithDefault[K, V1](underlying.updated(key, value), defaultValue)
164+
165+
override def concat [V2 >: V](xs: collection.IterableOnce[(K, V2)]^): WithDefault[K, V2] =
166+
new WithDefault( underlying.concat(xs) , defaultValue)
167+
168+
override def removed(key: K): WithDefault[K, V] = new WithDefault[K, V](underlying.removed(key), defaultValue)
169+
170+
override def empty: WithDefault[K, V] = new WithDefault[K, V](underlying.empty, defaultValue)
171+
172+
override protected def fromSpecific(coll: scala.collection.IterableOnce[(K, V) @uncheckedVariance]^): WithDefault[K, V] =
173+
new WithDefault[K, V](sortedMapFactory.from(coll), defaultValue)
174+
175+
override protected def newSpecificBuilder: Builder[(K, V), WithDefault[K, V]] @uncheckedVariance =
176+
SortedMap.newBuilder.mapResult((p: SortedMap[K, V]) => new WithDefault[K, V](p, defaultValue))
177+
}
178+
}

0 commit comments

Comments
 (0)