Skip to content

Commit 63246c9

Browse files
authored
Merge pull request scala/scala#6854 from mdedetrich/addLinkedVectorMap
Add SeqMap/VectorMap
2 parents 46bb107 + 29b8486 commit 63246c9

File tree

5 files changed

+390
-0
lines changed

5 files changed

+390
-0
lines changed

library/src/scala/collection/immutable/ListMap.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import scala.collection.mutable.{Builder, ImmutableBuilder}
4242
*/
4343
sealed class ListMap[K, +V]
4444
extends AbstractMap[K, V]
45+
with SeqMap[K, V]
4546
with MapOps[K, V, ListMap, ListMap[K, V]]
4647
with StrictOptimizedIterableOps[(K, V), Iterable, ListMap[K, V]]
4748
with StrictOptimizedMapOps[K, V, ListMap, ListMap[K, V]] {
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
package scala
2+
package collection
3+
package immutable
4+
5+
import java.io.{ObjectInputStream, ObjectOutputStream}
6+
7+
import scala.collection.mutable.{Builder, ImmutableBuilder}
8+
9+
/**
10+
* A generic trait for ordered immutable maps. Concrete classes have to provide
11+
* functionality for the abstract methods in `SeqMap`.
12+
*
13+
* Note that when checking for equality [[SeqMap]] does not take into account
14+
* ordering.
15+
*
16+
* @tparam K the type of the keys contained in this linked map.
17+
* @tparam V the type of the values associated with the keys in this linked map.
18+
*
19+
* @author Matthew de Detrich
20+
* @version 2.13
21+
* @since 2.13
22+
* @define coll immutable seq map
23+
* @define Coll `immutable.SeqMap`
24+
*/
25+
26+
trait SeqMap[K, +V]
27+
extends AbstractMap[K, V]
28+
with MapOps[K, V, SeqMap, SeqMap[K, V]]
29+
30+
object SeqMap extends MapFactory[SeqMap] {
31+
def empty[K, V]: SeqMap[K, V] = EmptyLinkedMap.asInstanceOf[SeqMap[K, V]]
32+
33+
def from[K, V](it: collection.IterableOnce[(K, V)]): SeqMap[K, V] =
34+
it match {
35+
case sm: SeqMap[K, V] => sm
36+
case _ => (newBuilder[K, V] ++= it).result()
37+
}
38+
39+
def newBuilder[K, V]: Builder[(K, V), SeqMap[K, V]] = VectorMap.newBuilder
40+
41+
@SerialVersionUID(3L)
42+
private object EmptyLinkedMap extends SeqMap[Any, Nothing] {
43+
override def size: Int = 0
44+
override def knownSize: Int = 0
45+
override def apply(key: Any) = throw new NoSuchElementException("key not found: " + key)
46+
override def contains(key: Any) = false
47+
def get(key: Any): Option[Nothing] = None
48+
override def getOrElse [V1](key: Any, default: => V1): V1 = default
49+
def iterator: Iterator[(Any, Nothing)] = Iterator.empty
50+
def updated [V1] (key: Any, value: V1): SeqMap[Any, V1] = new SeqMap1(key, value)
51+
def remove(key: Any): SeqMap[Any, Nothing] = this
52+
}
53+
54+
@SerialVersionUID(3L)
55+
final class SeqMap1[K, +V](key1: K, value1: V) extends SeqMap[K,V] with Serializable {
56+
override def size: Int = 1
57+
override def knownSize: Int = 1
58+
override def apply(key: K) = if (key == key1) value1 else throw new NoSuchElementException("key not found: " + key)
59+
override def contains(key: K) = key == key1
60+
def get(key: K): Option[V] =
61+
if (key == key1) Some(value1) else None
62+
override def getOrElse [V1 >: V](key: K, default: => V1): V1 =
63+
if (key == key1) value1 else default
64+
def iterator = Iterator.single((key1, value1))
65+
def updated[V1 >: V](key: K, value: V1): SeqMap[K, V1] =
66+
if (key == key1) new SeqMap1(key1, value)
67+
else new SeqMap2(key1, value1, key, value)
68+
def remove(key: K): SeqMap[K, V] =
69+
if (key == key1) SeqMap.empty else this
70+
override def foreach[U](f: ((K, V)) => U): Unit = {
71+
f((key1, value1))
72+
}
73+
}
74+
75+
@SerialVersionUID(3L)
76+
final class SeqMap2[K, +V](key1: K, value1: V, key2: K, value2: V) extends SeqMap[K,V] with Serializable {
77+
override def size: Int = 2
78+
override def knownSize: Int = 2
79+
override def apply(key: K) =
80+
if (key == key1) value1
81+
else if (key == key2) value2
82+
else throw new NoSuchElementException("key not found: " + key)
83+
override def contains(key: K) = (key == key1) || (key == key2)
84+
def get(key: K): Option[V] =
85+
if (key == key1) Some(value1)
86+
else if (key == key2) Some(value2)
87+
else None
88+
override def getOrElse [V1 >: V](key: K, default: => V1): V1 =
89+
if (key == key1) value1
90+
else if (key == key2) value2
91+
else default
92+
def iterator = ((key1, value1) :: (key2, value2) :: Nil).iterator
93+
def updated[V1 >: V](key: K, value: V1): SeqMap[K, V1] =
94+
if (key == key1) new SeqMap2(key1, value, key2, value2)
95+
else if (key == key2) new SeqMap2(key1, value1, key2, value)
96+
else new SeqMap3(key1, value1, key2, value2, key, value)
97+
def remove(key: K): SeqMap[K, V] =
98+
if (key == key1) new SeqMap1(key2, value2)
99+
else if (key == key2) new SeqMap1(key1, value1)
100+
else this
101+
override def foreach[U](f: ((K, V)) => U): Unit = {
102+
f((key1, value1)); f((key2, value2))
103+
}
104+
}
105+
106+
@SerialVersionUID(3L)
107+
class SeqMap3[K, +V](key1: K, value1: V, key2: K, value2: V, key3: K, value3: V) extends SeqMap[K,V] with Serializable {
108+
override def size: Int = 3
109+
override def knownSize: Int = 3
110+
override def apply(key: K) =
111+
if (key == key1) value1
112+
else if (key == key2) value2
113+
else if (key == key3) value3
114+
else throw new NoSuchElementException("key not found: " + key)
115+
override def contains(key: K) = (key == key1) || (key == key2) || (key == key3)
116+
def get(key: K): Option[V] =
117+
if (key == key1) Some(value1)
118+
else if (key == key2) Some(value2)
119+
else if (key == key3) Some(value3)
120+
else None
121+
override def getOrElse [V1 >: V](key: K, default: => V1): V1 =
122+
if (key == key1) value1
123+
else if (key == key2) value2
124+
else if (key == key3) value3
125+
else default
126+
def iterator = ((key1, value1) :: (key2, value2) :: (key3, value3) :: Nil).iterator
127+
def updated[V1 >: V](key: K, value: V1): SeqMap[K, V1] =
128+
if (key == key1) new SeqMap3(key1, value, key2, value2, key3, value3)
129+
else if (key == key2) new SeqMap3(key1, value1, key2, value, key3, value3)
130+
else if (key == key3) new SeqMap3(key1, value1, key2, value2, key3, value)
131+
else new SeqMap4(key1, value1, key2, value2, key3, value3, key, value)
132+
def remove(key: K): SeqMap[K, V] =
133+
if (key == key1) new SeqMap2(key2, value2, key3, value3)
134+
else if (key == key2) new SeqMap2(key1, value1, key3, value3)
135+
else if (key == key3) new SeqMap2(key1, value1, key2, value2)
136+
else this
137+
override def foreach[U](f: ((K, V)) => U): Unit = {
138+
f((key1, value1)); f((key2, value2)); f((key3, value3))
139+
}
140+
}
141+
142+
@SerialVersionUID(3L)
143+
final class SeqMap4[K, +V](key1: K, value1: V, key2: K, value2: V, key3: K, value3: V, key4: K, value4: V) extends SeqMap[K,V] with Serializable {
144+
override def size: Int = 4
145+
override def knownSize: Int = 4
146+
override def apply(key: K) =
147+
if (key == key1) value1
148+
else if (key == key2) value2
149+
else if (key == key3) value3
150+
else if (key == key4) value4
151+
else throw new NoSuchElementException("key not found: " + key)
152+
override def contains(key: K) = (key == key1) || (key == key2) || (key == key3) || (key == key4)
153+
def get(key: K): Option[V] =
154+
if (key == key1) Some(value1)
155+
else if (key == key2) Some(value2)
156+
else if (key == key3) Some(value3)
157+
else if (key == key4) Some(value4)
158+
else None
159+
override def getOrElse [V1 >: V](key: K, default: => V1): V1 =
160+
if (key == key1) value1
161+
else if (key == key2) value2
162+
else if (key == key3) value3
163+
else if (key == key4) value4
164+
else default
165+
def iterator = ((key1, value1) :: (key2, value2) :: (key3, value3) :: (key4, value4) :: Nil).iterator
166+
def updated[V1 >: V](key: K, value: V1): SeqMap[K, V1] =
167+
if (key == key1) new SeqMap4(key1, value, key2, value2, key3, value3, key4, value4)
168+
else if (key == key2) new SeqMap4(key1, value1, key2, value, key3, value3, key4, value4)
169+
else if (key == key3) new SeqMap4(key1, value1, key2, value2, key3, value, key4, value4)
170+
else if (key == key4) new SeqMap4(key1, value1, key2, value2, key3, value3, key4, value)
171+
else {
172+
// Directly create the elements for performance reasons
173+
val fields = Vector(key1, key2, key3, key4, key)
174+
val underlying = if (useBaseline)
175+
HashMap(
176+
(key1, (0, value1)),
177+
(key2, (1, value2)),
178+
(key3, (2, value3)),
179+
(key4, (3, value4)),
180+
(key, (4, value))
181+
)
182+
else
183+
ChampHashMap(
184+
(key1, (0, value1)),
185+
(key2, (1, value2)),
186+
(key3, (2, value3)),
187+
(key4, (3, value4)),
188+
(key, (4, value))
189+
)
190+
new VectorMap(fields, underlying)
191+
}
192+
def remove(key: K): SeqMap[K, V] =
193+
if (key == key1) new SeqMap3(key2, value2, key3, value3, key4, value4)
194+
else if (key == key2) new SeqMap3(key1, value1, key3, value3, key4, value4)
195+
else if (key == key3) new SeqMap3(key1, value1, key2, value2, key4, value4)
196+
else if (key == key4) new SeqMap3(key1, value1, key2, value2, key3, value3)
197+
else this
198+
override def foreach[U](f: ((K, V)) => U): Unit = {
199+
f((key1, value1)); f((key2, value2)); f((key3, value3)); f((key4, value4))
200+
}
201+
}
202+
203+
// getenv not getProperty for Scala.js friendliness.
204+
// TODO remove before 2.13.0-RC1? see scala/collection-strawman#572
205+
private final val useBaseline: Boolean =
206+
System.getenv("SCALA_COLLECTION_IMMUTABLE_USE_BASELINE") == "true"
207+
208+
// scalac generates a `readReplace` method to discard the deserialized state (see https://github.com/scala/bug/issues/10412).
209+
// This prevents it from serializing it in the first place:
210+
private[this] def writeObject(out: ObjectOutputStream): Unit = ()
211+
private[this] def readObject(in: ObjectInputStream): Unit = ()
212+
213+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package scala
2+
package collection
3+
package immutable
4+
5+
import java.io.{ObjectInputStream, ObjectOutputStream}
6+
7+
import scala.collection.mutable.{Builder, ImmutableBuilder}
8+
import scala.annotation.unchecked.{uncheckedVariance => uV}
9+
import scala.collection.immutable.Map.useBaseline
10+
11+
/** This class implements immutable maps using a vector/map-based data structure, which preserves insertion order.
12+
*
13+
* Unlike `ListMap`, `VectorMap` has amortized effectively constant lookup at the expense
14+
* of using extra memory and generally lower performance for other operations
15+
*
16+
* @tparam K the type of the keys contained in this vector map.
17+
* @tparam V the type of the values associated with the keys in this vector map.
18+
*
19+
* @author Matthew de Detrich
20+
* @version 2.13
21+
* @since 2.13
22+
* @define coll immutable vector map
23+
* @define Coll `immutable.VectorMap`
24+
*/
25+
final class VectorMap[K, +V] private[immutable] (
26+
private val fields: Vector[K],
27+
private val underlying: Map[K, (Int, V)])
28+
extends AbstractMap[K, V]
29+
with SeqMap[K, V]
30+
with MapOps[K, V, VectorMap, VectorMap[K, V]]
31+
with StrictOptimizedIterableOps[(K, V), Iterable, VectorMap[K, V]] {
32+
33+
override protected[this] def className: String = "VectorMap"
34+
35+
def updated[V1 >: V](key: K, value: V1): VectorMap[K, V1] = {
36+
underlying.get(key) match {
37+
case Some(oldIndexWithValue) =>
38+
new VectorMap(fields,
39+
underlying.updated(key, (oldIndexWithValue._1, value)))
40+
case None =>
41+
new VectorMap(
42+
fields :+ key,
43+
underlying.updated(key, (fields.length + 1, value)))
44+
}
45+
}
46+
47+
override def withDefault[V1 >: V](d: K => V1): Map.WithDefault[K, V1] =
48+
new Map.WithDefault(this, d)
49+
50+
override def withDefaultValue[V1 >: V](d: V1): Map.WithDefault[K, V1] = new Map.WithDefault[K, V1](this, _ => d)
51+
52+
def iterator: Iterator[(K, V)] = new Iterator[(K, V)] {
53+
private val fieldsIterator = fields.iterator
54+
55+
override def hasNext: Boolean = fieldsIterator.hasNext
56+
57+
override def next(): (K, V) = {
58+
val field = fieldsIterator.next()
59+
(field, underlying(field)._2)
60+
}
61+
}
62+
63+
def get(key: K): Option[V] = underlying.get(key) match {
64+
case Some(v) => Some(v._2)
65+
case None => None
66+
}
67+
68+
def remove(key: K): VectorMap[K, V] = {
69+
underlying.get(key) match {
70+
case Some((index, _)) =>
71+
new VectorMap(fields.patch(index, Nil, 1), underlying - key)
72+
case _ =>
73+
this
74+
}
75+
}
76+
77+
override def mapFactory: MapFactory[VectorMap] = VectorMap
78+
79+
override def size: Int = fields.size
80+
81+
override def knownSize: Int = fields.size
82+
83+
override def isEmpty: Boolean = fields.isEmpty
84+
85+
override final def contains(key: K): Boolean = underlying.contains(key)
86+
87+
override def head: (K, V) = iterator.next()
88+
89+
override def last: (K, V) = {
90+
val last = fields.last
91+
(last, underlying(last)._2)
92+
}
93+
94+
override def lastOption: Option[(K, V)] = {
95+
fields.lastOption match {
96+
case Some(last) => Some(last, underlying(last)._2)
97+
case None => None
98+
}
99+
}
100+
101+
override def tail: VectorMap[K, V] = {
102+
new VectorMap(fields.tail, underlying.remove(fields.last))
103+
}
104+
105+
override def init: VectorMap[K, V] = {
106+
new VectorMap(fields.init, underlying.remove(fields.head))
107+
}
108+
109+
// Only care about content, not ordering for equality
110+
override def equals(that: Any): Boolean =
111+
that match {
112+
case vmap: VectorMap[_, _] => underlying == vmap.underlying
113+
case _ => super.equals(that)
114+
}
115+
116+
override def foreach[U](f: ((K, V)) => U): Unit = iterator.foreach(f)
117+
118+
override def keys: Iterable[K] = fields.toIterable
119+
120+
override def values: Iterable[V] = new Iterable[V] {
121+
override def iterator: Iterator[V] = fields.iterator.map(underlying(_)._2)
122+
}
123+
}
124+
125+
object VectorMap extends MapFactory[VectorMap] {
126+
127+
def empty[K, V]: VectorMap[K, V] =
128+
new VectorMap[K, V](Vector.empty[K],
129+
if (VectorMap.useBaseline)
130+
HashMap.empty[K, (Int, V)]
131+
else
132+
ChampHashMap.empty[K, (Int, V)]
133+
)
134+
135+
def from[K, V](it: collection.IterableOnce[(K, V)]): VectorMap[K, V] =
136+
it match {
137+
case vm: VectorMap[K, V] => vm
138+
case _ => (newBuilder[K, V] ++= it).result()
139+
}
140+
141+
def newBuilder[K, V]: Builder[(K, V), VectorMap[K, V]] =
142+
new ImmutableBuilder[(K, V), VectorMap[K, V]](empty) {
143+
def addOne(elem: (K, V)): this.type = { elems = elems + elem; this }
144+
}
145+
146+
// getenv not getProperty for Scala.js friendliness.
147+
// TODO remove before 2.13.0-RC1? see scala/collection-strawman#572
148+
private final val useBaseline: Boolean =
149+
System.getenv("SCALA_COLLECTION_IMMUTABLE_USE_BASELINE") == "true"
150+
}

library/src/scala/collection/mutable/LinkedHashMap.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ object LinkedHashMap extends MapFactory[LinkedHashMap] {
4646
*/
4747
class LinkedHashMap[K, V]
4848
extends AbstractMap[K, V]
49+
with SeqMap[K, V]
4950
with MapOps[K, V, LinkedHashMap, LinkedHashMap[K, V]]
5051
with StrictOptimizedIterableOps[(K, V), Iterable, LinkedHashMap[K, V]]
5152
with StrictOptimizedMapOps[K, V, LinkedHashMap, LinkedHashMap[K, V]] {

0 commit comments

Comments
 (0)