Skip to content

Commit 86c26e2

Browse files
committed
Clone scala/collection/mutable/LinkedHash{Set,Map}.scala
1 parent ed54067 commit 86c26e2

File tree

2 files changed

+441
-0
lines changed

2 files changed

+441
-0
lines changed
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
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 mutable
16+
17+
import scala.annotation.nowarn
18+
import scala.collection.generic.DefaultSerializable
19+
20+
/** $factoryInfo
21+
* @define Coll `LinkedHashMap`
22+
* @define coll linked hash map
23+
*/
24+
@SerialVersionUID(3L)
25+
object LinkedHashMap extends MapFactory[LinkedHashMap] {
26+
27+
def empty[K, V] = new LinkedHashMap[K, V]
28+
29+
def from[K, V](it: collection.IterableOnce[(K, V)]) =
30+
it match {
31+
case lhm: LinkedHashMap[K, V] => lhm
32+
case _ => Growable.from(empty[K, V], it)
33+
}
34+
35+
def newBuilder[K, V] = new GrowableBuilder(empty[K, V])
36+
37+
/** Class for the linked hash map entry, used internally.
38+
*/
39+
private[mutable] final class LinkedEntry[K, V](val key: K, var value: V)
40+
extends HashEntry[K, LinkedEntry[K, V]] {
41+
var earlier: LinkedEntry[K, V] = null
42+
var later: LinkedEntry[K, V] = null
43+
}
44+
45+
}
46+
47+
/** This class implements mutable maps using a hashtable.
48+
* The iterator and all traversal methods of this class visit elements in the order they were inserted.
49+
*
50+
* @tparam K the type of the keys contained in this hash map.
51+
* @tparam V the type of the values assigned to keys in this hash map.
52+
*
53+
* @define Coll `LinkedHashMap`
54+
* @define coll linked hash map
55+
* @define mayNotTerminateInf
56+
* @define willNotTerminateInf
57+
* @define orderDependent
58+
* @define orderDependentFold
59+
*/
60+
class LinkedHashMap[K, V]
61+
extends AbstractMap[K, V]
62+
with SeqMap[K, V]
63+
with MapOps[K, V, LinkedHashMap, LinkedHashMap[K, V]]
64+
with StrictOptimizedIterableOps[(K, V), Iterable, LinkedHashMap[K, V]]
65+
with StrictOptimizedMapOps[K, V, LinkedHashMap, LinkedHashMap[K, V]]
66+
with MapFactoryDefaults[K, V, LinkedHashMap, Iterable]
67+
with DefaultSerializable {
68+
69+
override def mapFactory: MapFactory[LinkedHashMap] = LinkedHashMap
70+
71+
// stepper / keyStepper / valueStepper are not overridden to use XTableStepper because that stepper
72+
// would not return the elements in insertion order
73+
74+
private[collection] type Entry = LinkedHashMap.LinkedEntry[K, V]
75+
private[collection] def _firstEntry: Entry = firstEntry
76+
77+
@transient protected var firstEntry: Entry = null
78+
@transient protected var lastEntry: Entry = null
79+
@transient private[this] var table: HashTable[K, V, Entry] = newHashTable
80+
81+
// Used by scala-java8-compat (private[mutable] erases to public, so Java code can access it)
82+
private[mutable] def getTable: HashTable[K, V, Entry] = table
83+
84+
private def newHashTable =
85+
new HashTable[K, V, Entry] {
86+
def createNewEntry(key: K, value: V): Entry = {
87+
val e = new Entry(key, value)
88+
if (firstEntry eq null) firstEntry = e
89+
else { lastEntry.later = e; e.earlier = lastEntry }
90+
lastEntry = e
91+
e
92+
}
93+
94+
override def foreachEntry[U](f: Entry => U): Unit = {
95+
var cur = firstEntry
96+
while (cur ne null) {
97+
f(cur)
98+
cur = cur.later
99+
}
100+
}
101+
102+
}
103+
104+
override def last: (K, V) =
105+
if (size > 0) (lastEntry.key, lastEntry.value)
106+
else throw new NoSuchElementException("Cannot call .last on empty LinkedHashMap")
107+
108+
override def lastOption: Option[(K, V)] =
109+
if (size > 0) Some((lastEntry.key, lastEntry.value))
110+
else None
111+
112+
override def head: (K, V) =
113+
if (size > 0) (firstEntry.key, firstEntry.value)
114+
else throw new NoSuchElementException("Cannot call .head on empty LinkedHashMap")
115+
116+
override def headOption: Option[(K, V)] =
117+
if (size > 0) Some((firstEntry.key, firstEntry.value))
118+
else None
119+
120+
override def size = table.tableSize
121+
override def knownSize: Int = size
122+
override def isEmpty: Boolean = table.tableSize == 0
123+
def get(key: K): Option[V] = {
124+
val e = table.findEntry(key)
125+
if (e == null) None
126+
else Some(e.value)
127+
}
128+
129+
override def contains(key: K): Boolean = {
130+
if (getClass eq classOf[LinkedHashMap[_, _]])
131+
table.findEntry(key) != null
132+
else
133+
super.contains(key) // A subclass might override `get`, use the default implementation `contains`.
134+
}
135+
136+
override def put(key: K, value: V): Option[V] = {
137+
val e = table.findOrAddEntry(key, value)
138+
if (e eq null) None
139+
else { val v = e.value; e.value = value; Some(v) }
140+
}
141+
142+
override def update(key: K, value: V): Unit = {
143+
val e = table.findOrAddEntry(key, value)
144+
if (e ne null) e.value = value
145+
}
146+
147+
override def remove(key: K): Option[V] = {
148+
val e = table.removeEntry(key)
149+
if (e eq null) None
150+
else Some(remove0(e))
151+
}
152+
153+
private[this] def remove0(e: Entry): V = {
154+
if (e.earlier eq null) firstEntry = e.later
155+
else e.earlier.later = e.later
156+
if (e.later eq null) lastEntry = e.earlier
157+
else e.later.earlier = e.earlier
158+
e.earlier = null // Null references to prevent nepotism
159+
e.later = null
160+
e.value
161+
}
162+
163+
def addOne(kv: (K, V)): this.type = { put(kv._1, kv._2); this }
164+
165+
def subtractOne(key: K): this.type = { remove(key); this }
166+
167+
def iterator: Iterator[(K, V)] = new AbstractIterator[(K, V)] {
168+
private[this] var cur = firstEntry
169+
def hasNext = cur ne null
170+
def next() =
171+
if (hasNext) { val res = (cur.key, cur.value); cur = cur.later; res }
172+
else Iterator.empty.next()
173+
}
174+
175+
protected class LinkedKeySet extends KeySet {
176+
override def iterableFactory: IterableFactory[collection.Set] = LinkedHashSet
177+
}
178+
179+
override def keySet: collection.Set[K] = new LinkedKeySet
180+
181+
override def keysIterator: Iterator[K] = new AbstractIterator[K] {
182+
private[this] var cur = firstEntry
183+
def hasNext = cur ne null
184+
def next() =
185+
if (hasNext) { val res = cur.key; cur = cur.later; res }
186+
else Iterator.empty.next()
187+
}
188+
189+
// Override updateWith for performance, so we can do the update while hashing
190+
// the input key only once and performing one lookup into the hash table
191+
override def updateWith(key: K)(remappingFunction: Option[V] => Option[V]): Option[V] = {
192+
val keyIndex = table.index(table.elemHashCode(key))
193+
val entry = table.findEntry0(key, keyIndex)
194+
195+
val previousValue =
196+
if (entry == null) None
197+
else Some(entry.value)
198+
199+
val nextValue = remappingFunction(previousValue)
200+
201+
(previousValue, nextValue) match {
202+
case (None, None) => // do nothing
203+
case (Some(_), None) =>
204+
remove0(entry)
205+
table.removeEntry0(key, keyIndex)
206+
207+
case (None, Some(value)) =>
208+
table.addEntry0(table.createNewEntry(key, value), keyIndex)
209+
210+
case (Some(_), Some(value)) =>
211+
entry.value = value
212+
}
213+
214+
nextValue
215+
}
216+
217+
override def valuesIterator: Iterator[V] = new AbstractIterator[V] {
218+
private[this] var cur = firstEntry
219+
def hasNext = cur ne null
220+
def next() =
221+
if (hasNext) { val res = cur.value; cur = cur.later; res }
222+
else Iterator.empty.next()
223+
}
224+
225+
override def foreach[U](f: ((K, V)) => U): Unit = {
226+
var cur = firstEntry
227+
while (cur ne null) {
228+
f((cur.key, cur.value))
229+
cur = cur.later
230+
}
231+
}
232+
233+
override def foreachEntry[U](f: (K, V) => U): Unit = {
234+
var cur = firstEntry
235+
while (cur ne null) {
236+
f(cur.key, cur.value)
237+
cur = cur.later
238+
}
239+
}
240+
241+
override def clear(): Unit = {
242+
table.clearTable()
243+
firstEntry = null
244+
lastEntry = null
245+
}
246+
247+
private def writeObject(out: java.io.ObjectOutputStream): Unit = {
248+
out.defaultWriteObject()
249+
table.serializeTo(out, { entry =>
250+
out.writeObject(entry.key)
251+
out.writeObject(entry.value)
252+
})
253+
}
254+
255+
private def readObject(in: java.io.ObjectInputStream): Unit = {
256+
in.defaultReadObject()
257+
table = newHashTable
258+
table.init(in, table.createNewEntry(in.readObject().asInstanceOf[K], in.readObject().asInstanceOf[V]))
259+
}
260+
261+
@nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""")
262+
override protected[this] def stringPrefix = "LinkedHashMap"
263+
}
264+

0 commit comments

Comments
 (0)