Skip to content

Commit e780c3c

Browse files
committed
Add LinkedMap/VectorMap
1 parent 397901b commit e780c3c

File tree

3 files changed

+365
-0
lines changed

3 files changed

+365
-0
lines changed
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
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 `LinkedMap`:
12+
*
13+
* @tparam A
14+
* @tparam B
15+
*/
16+
17+
trait LinkedMap[K, +V]
18+
extends Map[K, V]
19+
with Iterable[(K, V)]
20+
with MapOps[K, V, LinkedMap, LinkedMap[K, V]]
21+
with Equals
22+
23+
object LinkedMap extends MapFactory[LinkedMap] {
24+
def empty[K, V]: LinkedMap[K, V] = EmptyLinkedMap.asInstanceOf[LinkedMap[K, V]]
25+
26+
def from[K, V](it: collection.IterableOnce[(K, V)]): LinkedMap[K, V] =
27+
it match {
28+
case vm: LinkedMap[K, V] => vm
29+
case _ => (newBuilder[K, V] ++= it).result()
30+
}
31+
32+
def newBuilder[K, V]: Builder[(K, V), LinkedMap[K, V]] =
33+
new ImmutableBuilder[(K, V), LinkedMap[K, V]](empty) {
34+
def addOne(elem: (K, V)): this.type = { elems = elems + elem; this }
35+
}
36+
37+
@SerialVersionUID(3L)
38+
private object EmptyLinkedMap extends LinkedMap[Any, Nothing] {
39+
override def size: Int = 0
40+
override def knownSize: Int = 0
41+
override def apply(key: Any) = throw new NoSuchElementException("key not found: " + key)
42+
override def contains(key: Any) = false
43+
def get(key: Any): Option[Nothing] = None
44+
override def getOrElse [V1](key: Any, default: => V1): V1 = default
45+
def iterator: Iterator[(Any, Nothing)] = Iterator.empty
46+
def updated [V1] (key: Any, value: V1): LinkedMap[Any, V1] = new LinkedMap1(key, value)
47+
def remove(key: Any): LinkedMap[Any, Nothing] = this
48+
}
49+
50+
@SerialVersionUID(3L)
51+
final class LinkedMap1[K, +V](key1: K, value1: V) extends LinkedMap[K,V] with Serializable {
52+
override def size: Int = 1
53+
override def knownSize: Int = 1
54+
override def apply(key: K) = if (key == key1) value1 else throw new NoSuchElementException("key not found: " + key)
55+
override def contains(key: K) = key == key1
56+
def get(key: K): Option[V] =
57+
if (key == key1) Some(value1) else None
58+
override def getOrElse [V1 >: V](key: K, default: => V1): V1 =
59+
if (key == key1) value1 else default
60+
def iterator = Iterator.single((key1, value1))
61+
def updated[V1 >: V](key: K, value: V1): LinkedMap[K, V1] =
62+
if (key == key1) new LinkedMap1(key1, value)
63+
else new LinkedMap2(key1, value1, key, value)
64+
def remove(key: K): LinkedMap[K, V] =
65+
if (key == key1) LinkedMap.empty else this
66+
override def foreach[U](f: ((K, V)) => U): Unit = {
67+
f((key1, value1))
68+
}
69+
}
70+
71+
@SerialVersionUID(3L)
72+
final class LinkedMap2[K, +V](key1: K, value1: V, key2: K, value2: V) extends LinkedMap[K,V] with Serializable {
73+
override def size: Int = 2
74+
override def knownSize: Int = 2
75+
override def apply(key: K) =
76+
if (key == key1) value1
77+
else if (key == key2) value2
78+
else throw new NoSuchElementException("key not found: " + key)
79+
override def contains(key: K) = (key == key1) || (key == key2)
80+
def get(key: K): Option[V] =
81+
if (key == key1) Some(value1)
82+
else if (key == key2) Some(value2)
83+
else None
84+
override def getOrElse [V1 >: V](key: K, default: => V1): V1 =
85+
if (key == key1) value1
86+
else if (key == key2) value2
87+
else default
88+
def iterator = ((key1, value1) :: (key2, value2) :: Nil).iterator
89+
def updated[V1 >: V](key: K, value: V1): LinkedMap[K, V1] =
90+
if (key == key1) new LinkedMap2(key1, value, key2, value2)
91+
else if (key == key2) new LinkedMap2(key1, value1, key2, value)
92+
else new LinkedMap3(key1, value1, key2, value2, key, value)
93+
def remove(key: K): LinkedMap[K, V] =
94+
if (key == key1) new LinkedMap1(key2, value2)
95+
else if (key == key2) new LinkedMap1(key1, value1)
96+
else this
97+
override def foreach[U](f: ((K, V)) => U): Unit = {
98+
f((key1, value1)); f((key2, value2))
99+
}
100+
}
101+
102+
@SerialVersionUID(3L)
103+
class LinkedMap3[K, +V](key1: K, value1: V, key2: K, value2: V, key3: K, value3: V) extends LinkedMap[K,V] with Serializable {
104+
override def size: Int = 3
105+
override def knownSize: Int = 3
106+
override def apply(key: K) =
107+
if (key == key1) value1
108+
else if (key == key2) value2
109+
else if (key == key3) value3
110+
else throw new NoSuchElementException("key not found: " + key)
111+
override def contains(key: K) = (key == key1) || (key == key2) || (key == key3)
112+
def get(key: K): Option[V] =
113+
if (key == key1) Some(value1)
114+
else if (key == key2) Some(value2)
115+
else if (key == key3) Some(value3)
116+
else None
117+
override def getOrElse [V1 >: V](key: K, default: => V1): V1 =
118+
if (key == key1) value1
119+
else if (key == key2) value2
120+
else if (key == key3) value3
121+
else default
122+
def iterator = ((key1, value1) :: (key2, value2) :: (key3, value3) :: Nil).iterator
123+
def updated[V1 >: V](key: K, value: V1): LinkedMap[K, V1] =
124+
if (key == key1) new LinkedMap3(key1, value, key2, value2, key3, value3)
125+
else if (key == key2) new LinkedMap3(key1, value1, key2, value, key3, value3)
126+
else if (key == key3) new LinkedMap3(key1, value1, key2, value2, key3, value)
127+
else new LinkedMap4(key1, value1, key2, value2, key3, value3, key, value)
128+
def remove(key: K): LinkedMap[K, V] =
129+
if (key == key1) new LinkedMap2(key2, value2, key3, value3)
130+
else if (key == key2) new LinkedMap2(key1, value1, key3, value3)
131+
else if (key == key3) new LinkedMap2(key1, value1, key2, value2)
132+
else this
133+
override def foreach[U](f: ((K, V)) => U): Unit = {
134+
f((key1, value1)); f((key2, value2)); f((key3, value3))
135+
}
136+
}
137+
138+
@SerialVersionUID(3L)
139+
final class LinkedMap4[K, +V](key1: K, value1: V, key2: K, value2: V, key3: K, value3: V, key4: K, value4: V) extends LinkedMap[K,V] with Serializable {
140+
override def size: Int = 4
141+
override def knownSize: Int = 4
142+
override def apply(key: K) =
143+
if (key == key1) value1
144+
else if (key == key2) value2
145+
else if (key == key3) value3
146+
else if (key == key4) value4
147+
else throw new NoSuchElementException("key not found: " + key)
148+
override def contains(key: K) = (key == key1) || (key == key2) || (key == key3) || (key == key4)
149+
def get(key: K): Option[V] =
150+
if (key == key1) Some(value1)
151+
else if (key == key2) Some(value2)
152+
else if (key == key3) Some(value3)
153+
else if (key == key4) Some(value4)
154+
else None
155+
override def getOrElse [V1 >: V](key: K, default: => V1): V1 =
156+
if (key == key1) value1
157+
else if (key == key2) value2
158+
else if (key == key3) value3
159+
else if (key == key4) value4
160+
else default
161+
def iterator = ((key1, value1) :: (key2, value2) :: (key3, value3) :: (key4, value4) :: Nil).iterator
162+
def updated[V1 >: V](key: K, value: V1): LinkedMap[K, V1] =
163+
if (key == key1) new LinkedMap4(key1, value, key2, value2, key3, value3, key4, value4)
164+
else if (key == key2) new LinkedMap4(key1, value1, key2, value, key3, value3, key4, value4)
165+
else if (key == key3) new LinkedMap4(key1, value1, key2, value2, key3, value, key4, value4)
166+
else if (key == key4) new LinkedMap4(key1, value1, key2, value2, key3, value3, key4, value)
167+
else {
168+
// Directly create the elements for performance reasons
169+
val fields = Vector(key1, key2, key3, key4, key)
170+
val underlying = if (useBaseline)
171+
HashMap(
172+
(key1, (0, value1)),
173+
(key2, (1, value2)),
174+
(key3, (2, value3)),
175+
(key4, (3, value4)),
176+
(key, (4, value))
177+
)
178+
else
179+
ChampHashMap(
180+
(key1, (0, value1)),
181+
(key2, (1, value2)),
182+
(key3, (2, value3)),
183+
(key4, (3, value4)),
184+
(key, (4, value))
185+
)
186+
new VectorMap(fields, underlying)
187+
}
188+
def remove(key: K): LinkedMap[K, V] =
189+
if (key == key1) new LinkedMap3(key2, value2, key3, value3, key4, value4)
190+
else if (key == key2) new LinkedMap3(key1, value1, key3, value3, key4, value4)
191+
else if (key == key3) new LinkedMap3(key1, value1, key2, value2, key4, value4)
192+
else if (key == key4) new LinkedMap3(key1, value1, key2, value2, key3, value3)
193+
else this
194+
override def foreach[U](f: ((K, V)) => U): Unit = {
195+
f((key1, value1)); f((key2, value2)); f((key3, value3)); f((key4, value4))
196+
}
197+
}
198+
199+
// getenv not getProperty for Scala.js friendliness.
200+
// TODO remove before 2.13.0-RC1? see scala/collection-strawman#572
201+
private final val useBaseline: Boolean =
202+
System.getenv("SCALA_COLLECTION_IMMUTABLE_USE_BASELINE") == "true"
203+
204+
// scalac generates a `readReplace` method to discard the deserialized state (see https://github.com/scala/bug/issues/10412).
205+
// This prevents it from serializing it in the first place:
206+
private[this] def writeObject(out: ObjectOutputStream): Unit = ()
207+
private[this] def readObject(in: ObjectInputStream): Unit = ()
208+
209+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import scala.collection.mutable.{Builder, ImmutableBuilder}
4343
*/
4444
sealed class ListMap[K, +V]
4545
extends AbstractMap[K, V]
46+
with LinkedMap[K, V]
4647
with MapOps[K, V, ListMap, ListMap[K, V]]
4748
with StrictOptimizedIterableOps[(K, V), Iterable, ListMap[K, V]] {
4849

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
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+
val fields: Vector[K],
27+
val underlying: Map[K, (Int, V)])
28+
extends AbstractMap[K, V]
29+
with LinkedMap[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+
override def updated[V1 >: V](key: K, value: V1): VectorMap[K, V1] = {
36+
if (underlying.get(key).isDefined) {
37+
val oldKey = underlying(key)._1
38+
new VectorMap(fields,
39+
underlying.updated(key, (oldKey, value.asInstanceOf[V])))
40+
} else {
41+
new VectorMap(
42+
fields :+ key,
43+
underlying.updated(key, (fields.length + 1, value.asInstanceOf[V])))
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+
override 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+
override def get(key: K): Option[V] = underlying.get(key) match {
64+
case Some(v) => Some(v._2)
65+
case None => None
66+
}
67+
68+
override 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+
val tail = fields.last
103+
104+
new VectorMap(fields.slice(1, fields.size), underlying.remove(tail))
105+
}
106+
107+
override def init: VectorMap[K, V] = {
108+
val head = fields.head
109+
110+
new VectorMap(fields.slice(1, fields.size - 0), underlying.remove(head))
111+
}
112+
113+
// Only care about content, not ordering for equality
114+
override def equals(that: Any): Boolean =
115+
that match {
116+
case vmap: VectorMap[K, V] =>
117+
underlying == vmap.underlying
118+
case _ => super.equals(that)
119+
}
120+
121+
override def foreach[U](f: ((K, V)) => U): Unit = iterator.foreach(f)
122+
123+
override def keys: Iterable[K] = fields.toIterable
124+
125+
override def values: Iterable[V] = new Iterable[V] {
126+
override def iterator: Iterator[V] = fields.iterator.map(underlying(_)._2)
127+
}
128+
}
129+
130+
object VectorMap extends MapFactory[VectorMap] {
131+
132+
def empty[K, V]: VectorMap[K, V] =
133+
new VectorMap[K, V](Vector.empty[K],
134+
if (VectorMap.useBaseline)
135+
HashMap.empty[K, (Int, V)]
136+
else
137+
ChampHashMap.empty[K, (Int, V)]
138+
)
139+
140+
def from[K, V](it: collection.IterableOnce[(K, V)]): VectorMap[K, V] =
141+
it match {
142+
case vm: VectorMap[K, V] => vm
143+
case _ => (newBuilder[K, V] ++= it).result()
144+
}
145+
146+
def newBuilder[K, V]: Builder[(K, V), VectorMap[K, V]] =
147+
new ImmutableBuilder[(K, V), VectorMap[K, V]](empty) {
148+
def addOne(elem: (K, V)): this.type = { elems = elems + elem; this }
149+
}
150+
151+
// getenv not getProperty for Scala.js friendliness.
152+
// TODO remove before 2.13.0-RC1? see scala/collection-strawman#572
153+
private final val useBaseline: Boolean =
154+
System.getenv("SCALA_COLLECTION_IMMUTABLE_USE_BASELINE") == "true"
155+
}

0 commit comments

Comments
 (0)