Skip to content

Commit 783ea88

Browse files
committed
Properly _not_ capture *this* inside the KeySet implementation
Also, provide a LazyKeySet implementation when the Map is strict.
1 parent 703cceb commit 783ea88

File tree

1 file changed

+28
-9
lines changed

1 file changed

+28
-9
lines changed

library/src/scala/collection/Map.scala

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,21 @@ transparent trait StrictMapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _]
127127
mapFactory.from(new View.Concat(thatIterable, this))
128128
}
129129

130+
// The original keySet implementation, with a lazy iterator over the keys,
131+
// is only correct if we have a strict Map.
132+
// We restore it here.
133+
override def keySet: Set[K] = new LazyKeySet
130134

135+
/** The implementation class of the set returned by `keySet`, for pure maps.
136+
*/
137+
private class LazyKeySet extends AbstractSet[K] with DefaultSerializable {
138+
def diff(that: Set[K]): Set[K] = LazyKeySet.this.fromSpecific(this.view.filterNot(that))
139+
def iterator: Iterator[K] = StrictMapOps.this.keysIterator
140+
def contains(key: K): Boolean = StrictMapOps.this.contains(key)
141+
override def size: Int = StrictMapOps.this.size
142+
override def knownSize: Int = StrictMapOps.this.knownSize
143+
override def isEmpty: Boolean = StrictMapOps.this.isEmpty
144+
}
131145
}
132146

133147
/** Base Map implementation type
@@ -239,19 +253,24 @@ transparent trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C]
239253
/** The implementation class of the set returned by `keySet`.
240254
*/
241255
protected class KeySet extends AbstractSet[K] with GenKeySet with DefaultSerializable {
242-
def diff(that: Set[K]): Set[K] = fromSpecific(this.view.filterNot(that))
256+
// If you need a generic, capturing KeySet, create a View from keysIterator
257+
def diff(that: Set[K]): Set[K] = fromSpecific(allKeys.filterNot(that))
243258
}
244259

245-
/** A generic trait that is reused by keyset implementations */
260+
/** A generic trait that is reused by keyset implementations.
261+
* Note that this version of KeySet copies all the keys into an interval val.
262+
* See [[StrictMapOps.LazyKeySet]] for a version that lazily captures the map.
263+
*/
246264
protected trait GenKeySet { this: Set[K] =>
265+
// CC note: this is unavoidable to make the KeySet pure.
266+
private[MapOps] val allKeys = MapOps.this.keysIterator.toSet
267+
// We restore the lazy behavior in StrictMapOps
247268
def iterator: Iterator[K] =
248-
// CC note: this is unavoidable to make the KeySet pure.
249-
// If you need a generic, capturing KeySet, create a View from keysIterator
250-
MapOps.this.keysIterator.toSet.iterator
251-
def contains(key: K): Boolean = MapOps.this.contains(key)
252-
override def size: Int = MapOps.this.size
253-
override def knownSize: Int = MapOps.this.knownSize
254-
override def isEmpty: Boolean = MapOps.this.isEmpty
269+
allKeys.iterator
270+
def contains(key: K): Boolean = allKeys.contains(key)
271+
override def size: Int = allKeys.size
272+
override def knownSize: Int = allKeys.knownSize
273+
override def isEmpty: Boolean = allKeys.isEmpty
255274
}
256275

257276
/** An [[Iterable]] collection of the keys contained by this map.

0 commit comments

Comments
 (0)