Skip to content

Commit 02c6281

Browse files
committed
Add immutable ListSet and ListMap to stdlib
1 parent 528c249 commit 02c6281

File tree

2 files changed

+513
-0
lines changed

2 files changed

+513
-0
lines changed
Lines changed: 373 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
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.tailrec
18+
import scala.collection.mutable.ReusableBuilder
19+
import scala.collection.generic.DefaultSerializable
20+
import scala.runtime.Statics.releaseFence
21+
import scala.util.hashing.MurmurHash3
22+
import language.experimental.captureChecking
23+
import scala.annotation.unchecked.uncheckedCaptures
24+
25+
/**
26+
* This class implements immutable maps using a list-based data structure. List map iterators and
27+
* traversal methods visit key-value pairs in the order they were first inserted.
28+
*
29+
* Entries are stored internally in reversed insertion order, which means the newest key is at the
30+
* head of the list. As such, methods such as `head` and `tail` are O(n), while `last` and `init`
31+
* are O(1). Other operations, such as inserting or removing entries, are also O(n), which makes
32+
* this collection suitable only for a small number of elements.
33+
*
34+
* Instances of `ListMap` represent empty maps; they can be either created by calling the
35+
* constructor directly, or by applying the function `ListMap.empty`.
36+
*
37+
* @tparam K the type of the keys contained in this list map
38+
* @tparam V the type of the values associated with the keys
39+
*
40+
* @define Coll ListMap
41+
* @define coll list map
42+
* @define mayNotTerminateInf
43+
* @define willNotTerminateInf
44+
*/
45+
sealed class ListMap[K, +V]
46+
extends AbstractMap[K, V]
47+
with SeqMap[K, V]
48+
with StrictOptimizedMapOps[K, V, ListMap, ListMap[K, V]]
49+
with MapFactoryDefaults[K, V, ListMap, Iterable]
50+
with DefaultSerializable {
51+
52+
override def mapFactory: MapFactory[ListMap] = ListMap
53+
54+
override def size: Int = 0
55+
56+
override def isEmpty: Boolean = true
57+
58+
override def knownSize: Int = 0
59+
def get(key: K): Option[V] = None
60+
61+
def updated[V1 >: V](key: K, value: V1): ListMap[K, V1] = new ListMap.Node[K, V1](key, value, this)
62+
63+
def removed(key: K): ListMap[K, V] = this
64+
65+
def iterator: Iterator[(K, V)] = {
66+
var curr: ListMap[K, V] = this
67+
var res: List[(K, V)] = Nil
68+
while (curr.nonEmpty) {
69+
res = (curr.key, curr.value) :: res
70+
curr = curr.next
71+
}
72+
res.iterator
73+
}
74+
75+
override def keys: Iterable[K] = {
76+
var curr: ListMap[K, V] = this
77+
var res: List[K] = Nil
78+
while (curr.nonEmpty) {
79+
res = curr.key :: res
80+
curr = curr.next
81+
}
82+
res
83+
}
84+
85+
override def hashCode(): Int = {
86+
if (isEmpty) MurmurHash3.emptyMapHash
87+
else {
88+
// Can't efficiently override foreachEntry directly in ListMap because it would need to preserve iteration
89+
// order be reversing the list first. But mapHash is symmetric so the reversed order is fine here.
90+
val _reversed = new immutable.AbstractMap[K, V] {
91+
override def isEmpty: Boolean = ListMap.this.isEmpty
92+
override def removed(key: K): Map[K, V] = ListMap.this.removed(key)
93+
override def updated[V1 >: V](key: K, value: V1): Map[K, V1] = ListMap.this.updated(key, value)
94+
override def get(key: K): Option[V] = ListMap.this.get(key)
95+
override def iterator: Iterator[(K, V)] = ListMap.this.iterator
96+
override def foreachEntry[U](f: (K, V) => U): Unit = {
97+
var curr: ListMap[K, V] = ListMap.this
98+
while (curr.nonEmpty) {
99+
f(curr.key, curr.value)
100+
curr = curr.next
101+
}
102+
}
103+
}
104+
MurmurHash3.mapHash(_reversed)
105+
}
106+
}
107+
108+
private[immutable] def key: K = throw new NoSuchElementException("key of empty map")
109+
private[immutable] def value: V = throw new NoSuchElementException("value of empty map")
110+
private[immutable] def next: ListMap[K, V] = throw new NoSuchElementException("next of empty map")
111+
112+
override def foldRight[Z](z: Z)(op: ((K, V), Z) => Z): Z = ListMap.foldRightInternal(this, z, op)
113+
override protected[this] def className = "ListMap"
114+
115+
}
116+
117+
/**
118+
* $factoryInfo
119+
*
120+
* Note that each element insertion takes O(n) time, which means that creating a list map with
121+
* n elements will take O(n^2^) time. This makes the builder suitable only for a small number of
122+
* elements.
123+
*
124+
* @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#list-maps "Scala's Collection Library overview"]]
125+
* section on `List Maps` for more information.
126+
* @define Coll ListMap
127+
* @define coll list map
128+
*/
129+
@SerialVersionUID(3L)
130+
object ListMap extends MapFactory[ListMap] {
131+
/**
132+
* Represents an entry in the `ListMap`.
133+
*/
134+
private[immutable] final class Node[K, V](
135+
override private[immutable] val key: K,
136+
private[immutable] var _value: V @uncheckedCaptures,
137+
private[immutable] var _init: ListMap[K, V] @uncheckedCaptures
138+
) extends ListMap[K, V] {
139+
releaseFence()
140+
141+
override private[immutable] def value: V = _value
142+
143+
override def size: Int = sizeInternal(this, 0)
144+
145+
@tailrec private[this] def sizeInternal(cur: ListMap[K, V], acc: Int): Int =
146+
if (cur.isEmpty) acc
147+
else sizeInternal(cur.next, acc + 1)
148+
149+
override def isEmpty: Boolean = false
150+
151+
override def knownSize: Int = -1
152+
153+
@throws[NoSuchElementException]
154+
override def apply(k: K): V = applyInternal(this, k)
155+
156+
@tailrec private[this] def applyInternal(cur: ListMap[K, V], k: K): V =
157+
if (cur.isEmpty) throw new NoSuchElementException("key not found: " + k)
158+
else if (k == cur.key) cur.value
159+
else applyInternal(cur.next, k)
160+
161+
override def get(k: K): Option[V] = getInternal(this, k)
162+
163+
@tailrec private[this] def getInternal(cur: ListMap[K, V], k: K): Option[V] =
164+
if (cur.isEmpty) None
165+
else if (k == cur.key) Some(cur.value)
166+
else getInternal(cur.next, k)
167+
168+
override def contains(k: K): Boolean = containsInternal(this, k)
169+
170+
@tailrec private[this] def containsInternal(cur: ListMap[K, V], k: K): Boolean =
171+
if (cur.isEmpty) false
172+
else if (k == cur.key) true
173+
else containsInternal(cur.next, k)
174+
175+
override def updated[V1 >: V](k: K, v: V1): ListMap[K, V1] = {
176+
177+
var index = -1 // the index (in reverse) where the key to update exists, if it is found
178+
var found = false // true if the key is found int he map
179+
var isDifferent = false // true if the key was found and the values are different
180+
181+
{
182+
var curr: ListMap[K, V] = this
183+
184+
while (curr.nonEmpty && !found) {
185+
if (k == curr.key) {
186+
found = true
187+
isDifferent = v.asInstanceOf[AnyRef] ne curr.value.asInstanceOf[AnyRef]
188+
}
189+
index += 1
190+
curr = curr.init
191+
}
192+
}
193+
194+
if (found) {
195+
if (isDifferent) {
196+
var newHead: ListMap.Node[K, V1] = null
197+
var prev: ListMap.Node[K, V1] = null
198+
var curr: ListMap[K, V1] = this
199+
var i = 0
200+
while (i < index) {
201+
val temp = new ListMap.Node(curr.key, curr.value, null)
202+
if (prev ne null) {
203+
prev._init = temp
204+
}
205+
prev = temp
206+
curr = curr.init
207+
if (newHead eq null) {
208+
newHead = prev
209+
}
210+
i += 1
211+
}
212+
val newNode = new ListMap.Node(curr.key, v, curr.init)
213+
if (prev ne null) {
214+
prev._init = newNode
215+
}
216+
releaseFence()
217+
if (newHead eq null) newNode else newHead
218+
} else {
219+
this
220+
}
221+
} else {
222+
new ListMap.Node(k, v, this)
223+
}
224+
}
225+
226+
@tailrec private[this] def removeInternal(k: K, cur: ListMap[K, V], acc: List[ListMap[K, V]]): ListMap[K, V] =
227+
if (cur.isEmpty) acc.last
228+
else if (k == cur.key) acc.foldLeft(cur.next) { (t, h) => new Node(h.key, h.value, t) }
229+
else removeInternal(k, cur.next, cur :: acc)
230+
231+
override def removed(k: K): ListMap[K, V] = removeInternal(k, this, Nil)
232+
233+
override private[immutable] def next: ListMap[K, V] = _init
234+
235+
override def last: (K, V) = (key, value)
236+
override def init: ListMap[K, V] = next
237+
238+
}
239+
240+
def empty[K, V]: ListMap[K, V] = EmptyListMap.asInstanceOf[ListMap[K, V]]
241+
242+
private object EmptyListMap extends ListMap[Any, Nothing]
243+
244+
def from[K, V](it: collection.IterableOnce[(K, V)]^): ListMap[K, V] =
245+
it match {
246+
case lm: ListMap[K, V] => lm
247+
case lhm: collection.mutable.LinkedHashMap[K, V] =>
248+
// by directly iterating through LinkedHashMap entries, we save creating intermediate tuples for each
249+
// key-value pair
250+
var current: ListMap[K, V] = empty[K, V]
251+
var firstEntry = lhm._firstEntry
252+
while (firstEntry ne null) {
253+
current = new Node(firstEntry.key, firstEntry.value, current)
254+
firstEntry = firstEntry.later
255+
}
256+
current
257+
case _: collection.Map[K, V] | _: collection.MapView[K, V] =>
258+
// when creating from a map, we need not handle duplicate keys, so we can just append each key-value to the end
259+
var current: ListMap[K, V] = empty[K, V]
260+
val iter = it.iterator
261+
while (iter.hasNext) {
262+
val (k, v) = iter.next()
263+
current = new Node(k, v, current)
264+
}
265+
current
266+
267+
case _ => (newBuilder[K, V] ++= it).result()
268+
}
269+
270+
/** Returns a new ListMap builder
271+
*
272+
* The implementation safely handles additions after `result()` without calling `clear()`
273+
*
274+
* @tparam K the map key type
275+
* @tparam V the map value type
276+
*/
277+
def newBuilder[K, V]: ReusableBuilder[(K, V), ListMap[K, V]] = new ListMapBuilder[K, V]
278+
279+
@tailrec private def foldRightInternal[K, V, Z](map: ListMap[K, V], prevValue: Z, op: ((K, V), Z) => Z): Z = {
280+
if (map.isEmpty) prevValue
281+
else foldRightInternal(map.init, op(map.last, prevValue), op)
282+
}
283+
}
284+
285+
/** Builder for ListMap.
286+
* $multipleResults
287+
*/
288+
private[immutable] final class ListMapBuilder[K, V] extends mutable.ReusableBuilder[(K, V), ListMap[K, V]] {
289+
private[this] var isAliased: Boolean = false
290+
private[this] var underlying: ListMap[K, V] @uncheckedCaptures = ListMap.empty
291+
292+
override def clear(): Unit = {
293+
underlying = ListMap.empty
294+
isAliased = false
295+
}
296+
297+
override def result(): ListMap[K, V] = {
298+
isAliased = true
299+
releaseFence()
300+
underlying
301+
}
302+
303+
override def addOne(elem: (K, V)): this.type = addOne(elem._1, elem._2)
304+
305+
@tailrec
306+
private[this] def insertValueAtKeyReturnFound(m: ListMap[K, V], key: K, value: V): Boolean = m match {
307+
case n: ListMap.Node[K, V] =>
308+
if (n.key == key) {
309+
n._value = value
310+
true
311+
} else {
312+
insertValueAtKeyReturnFound(n.init, key, value)
313+
}
314+
case _ => false
315+
}
316+
317+
def addOne(key: K, value: V): this.type = {
318+
if (isAliased) {
319+
underlying = underlying.updated(key, value)
320+
} else {
321+
if (!insertValueAtKeyReturnFound(underlying, key, value)) {
322+
underlying = new ListMap.Node(key, value, underlying)
323+
}
324+
}
325+
this
326+
}
327+
override def addAll(xs: IterableOnce[(K, V)]^): this.type = {
328+
if (isAliased) {
329+
super.addAll(xs)
330+
} else if (underlying.nonEmpty) {
331+
xs match {
332+
case m: collection.Map[K, V] =>
333+
// if it is a map, then its keys will not collide with themselves.
334+
// therefor we only need to check the already-existing elements for collisions.
335+
// No need to check the entire list
336+
337+
val iter = m.iterator
338+
var newUnderlying = underlying
339+
while (iter.hasNext) {
340+
val next = iter.next()
341+
if (!insertValueAtKeyReturnFound(underlying, next._1, next._2)) {
342+
newUnderlying = new ListMap.Node[K, V](next._1, next._2, newUnderlying)
343+
}
344+
}
345+
underlying = newUnderlying
346+
this
347+
348+
case _ =>
349+
super.addAll(xs)
350+
}
351+
} else xs match {
352+
case lhm: collection.mutable.LinkedHashMap[K, V] =>
353+
// special-casing LinkedHashMap avoids creating of Iterator and tuples for each key-value
354+
var firstEntry = lhm._firstEntry
355+
while (firstEntry ne null) {
356+
underlying = new ListMap.Node(firstEntry.key, firstEntry.value, underlying)
357+
firstEntry = firstEntry.later
358+
}
359+
this
360+
361+
case _: collection.Map[K, V] | _: collection.MapView[K, V] =>
362+
val iter = xs.iterator
363+
while (iter.hasNext) {
364+
val (k, v) = iter.next()
365+
underlying = new ListMap.Node(k, v, underlying)
366+
}
367+
368+
this
369+
case _ =>
370+
super.addAll(xs)
371+
}
372+
}
373+
}

0 commit comments

Comments
 (0)