Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions library/src/scala/collection/Map.scala
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ transparent trait MapOps[K, +V, +CC[_, _] <: IterableOps[?, AnyConstr, ?], +C]
* - To ensure an independent strict collection, use `m.keysIterator.toSet`
* - To obtain a view on the keys, use `scala.collection.View.fromIteratorProvider(m.keysIterator)`
*
* Specifically, for mutable collections, it is **not** guaranteed that the set would reflect the changes made onto
* the map, **nor** it is guaranteed that the set would _not_ reflect the changes. To guarantee either behavior,
* obtain a strict collection or a view as above.
*
* @return a set representing the keys contained by this map
*/
def keySet: Set[K] =
Expand All @@ -216,7 +220,7 @@ transparent trait MapOps[K, +V, +CC[_, _] <: IterableOps[?, AnyConstr, ?], +C]
*/
protected trait GenKeySet { this: Set[K] =>
// CC note: this is unavoidable to make the KeySet pure.
private[MapOps] val allKeys = MapOps.this.keysIterator.toList
private[MapOps] val allKeys = MapOps.this.keysIterator.to(mutable.LinkedHashSet)
// We restore the lazy behavior in LazyKeySet
def iterator: Iterator[K] =
allKeys.iterator
Expand Down Expand Up @@ -426,7 +430,7 @@ object MapOps {

/** The implementation class of the set returned by `keySet`, for pure maps.
*/
private class LazyKeySet[K, +V, +CC[_, _] <: IterableOps[?, AnyConstr, ?], +C](mp: MapOps[K, V, CC, C]) extends AbstractSet[K] with DefaultSerializable {
private[collection] class LazyKeySet[K, +V, +CC[_, _] <: IterableOps[?, AnyConstr, ?], +C](mp: MapOps[K, V, CC, C]) extends AbstractSet[K] with DefaultSerializable {
def iterator: Iterator[K] = mp.keysIterator
def diff(that: Set[K]): Set[K] = LazyKeySet.this.fromSpecific(this.view.filterNot(that))
def contains(key: K): Boolean = mp.contains(key)
Expand Down
16 changes: 15 additions & 1 deletion library/src/scala/collection/SortedMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,22 @@ transparent trait SortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] & SortedMapOps[X, Y
rangeUntil(next)
}

override def keySet: SortedSet[K] = new KeySortedSet
override def keySet: SortedSet[K] = new LazyKeySortedSet

/** The implementation class of the set returned by `keySet` */
private[collection] class LazyKeySortedSet extends MapOps.LazyKeySet(this) with SortedSet[K] {
implicit def ordering: Ordering[K] = SortedMapOps.this.ordering
def iteratorFrom(start: K): Iterator[K] = SortedMapOps.this.keysIteratorFrom(start)

override def diff(that: Set[K]): SortedSet[K] = fromSpecific(view.filterNot(that))
override def rangeImpl(from: Option[K], until: Option[K]): SortedSet[K] = {
val map = SortedMapOps.this.rangeImpl(from, until)
new map.LazyKeySortedSet
}
}

/** The old implementation class of the set returned by `keySet` */
@deprecated("KeySortedSet is no longer used in the .keySet implementation", since = "3.8.0")
protected class KeySortedSet extends SortedSet[K] with GenKeySet with GenKeySortedSet {
def diff(that: Set[K]): SortedSet[K] = fromSpecific(view.filterNot(that))
def rangeImpl(from: Option[K], until: Option[K]): SortedSet[K] = {
Expand All @@ -144,6 +157,7 @@ transparent trait SortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] & SortedMapOps[X, Y
}

/** A generic trait that is reused by sorted keyset implementations */
@deprecated("GenKeySortedSet is no longer used in .keySet implementations", since = "3.8.0")
protected trait GenKeySortedSet extends GenKeySet { this: SortedSet[K] =>
implicit def ordering: Ordering[K] = SortedMapOps.this.ordering
def iteratorFrom(start: K): Iterator[K] = SortedMapOps.this.keysIteratorFrom(start)
Expand Down
3 changes: 2 additions & 1 deletion library/src/scala/collection/immutable/HashMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ final class HashMap[K, +V] private[immutable] (private[immutable] val rootNode:

override def keySet: Set[K] = if (size == 0) Set.empty else new HashKeySet

private[immutable] final class HashKeySet extends ImmutableKeySet {

private[immutable] final class HashKeySet extends LazyImmutableKeySet {

private def newKeySetOrThis(newHashMap: HashMap[K, ?]): Set[K] =
if (newHashMap eq HashMap.this) this else newHashMap.keySet
Expand Down
10 changes: 9 additions & 1 deletion library/src/scala/collection/immutable/Map.scala
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,17 @@ transparent trait MapOps[K, +V, +CC[X, +Y] <: MapOps[X, Y, CC, ?], +C <: MapOps[
*/
def transform[W](f: (K, V) => W): CC[K, W] = map { case (k, v) => (k, f(k, v)) }

override def keySet: Set[K] = new ImmutableKeySet
override def keySet: Set[K] = new LazyImmutableKeySet

/** The implementation class of the set returned by `keySet` */
private[immutable] class LazyImmutableKeySet extends MapOps.LazyKeySet(this) with Set[K] {
override def diff(that: collection.Set[K]): Set[K] = super.diff(that)
override def incl(elem: K): Set[K] = if (this(elem)) this else MapOps.this.updated(elem, ()).keySet
override def excl(elem: K): Set[K] = if (this(elem)) MapOps.this.removed(elem).keySet else this
}

/** The implementation class of the set returned by `keySet` */
@deprecated("ImmutableKeySet is no longer used in .keySet implementations", since = "3.8.0")
protected[immutable] class ImmutableKeySet extends AbstractSet[K] with GenKeySet with DefaultSerializable {
def incl(elem: K): Set[K] = if (this(elem)) this else empty ++ this + elem
def excl(elem: K): Set[K] = if (this(elem)) empty ++ this - elem else this
Expand Down
2 changes: 1 addition & 1 deletion library/src/scala/collection/immutable/Set.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ object Set extends IterableFactory[Set] {
case s: Set3[E] => s
case s: Set4[E] => s
case s: HashMap[E @unchecked, _]#HashKeySet => s
case s: MapOps[E, Any, Map, Map[E, Any]]#ImmutableKeySet @unchecked => s
case s: MapOps[E, Any, Map, Map[E, Any]]#LazyImmutableKeySet @unchecked => s
// We also want `SortedSet` (and subclasses, such as `BitSet`)
// to rebuild themselves, to avoid element type widening issues.
case _ => newBuilder[E].addAll(it).result()
Expand Down
14 changes: 13 additions & 1 deletion library/src/scala/collection/immutable/SortedMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,21 @@ transparent trait SortedMapOps[K, +V, +CC[X, +Y] <: Map[X, Y] & SortedMapOps[X,

def unsorted: Map[K, V]

override def keySet: SortedSet[K] = new ImmutableKeySortedSet
override def keySet: SortedSet[K] = new LazyImmutableKeySortedSet

/** The implementation class of the set returned by `keySet` */
private class LazyImmutableKeySortedSet extends LazyKeySortedSet with SortedSet[K] {
override def diff(that: scala.collection.Set[K]): SortedSet[K] = super.diff(that)
override def rangeImpl(from: Option[K], until: Option[K]): SortedSet[K] = {
val map = self.rangeImpl(from, until)
new map.LazyImmutableKeySortedSet
}
def incl(elem: K): SortedSet[K] = fromSpecific(this).incl(elem)
def excl(elem: K): SortedSet[K] = fromSpecific(this).excl(elem)
}

/** The implementation class of the set returned by `keySet` */
@deprecated("ImmutableKeySortedSet is no longer used by the .keySet implementation", since = "3.8.0")
protected class ImmutableKeySortedSet extends AbstractSet[K] with SortedSet[K] with GenKeySet with GenKeySortedSet {
def rangeImpl(from: Option[K], until: Option[K]): SortedSet[K] = {
val map = self.rangeImpl(from, until)
Expand Down
6 changes: 5 additions & 1 deletion library/src/scala/collection/mutable/LinkedHashMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,15 @@ class LinkedHashMap[K, V]
def extract(nd: Entry): (K, V) = (nd.key, nd.value)
}

@deprecated("LinkedKeySet is now strict and no longer used in the implementation of .keySet", "3.8.0")
/** Note that a LinkedKeySet could be strict. */
protected class LinkedKeySet extends KeySet {
override def iterableFactory: IterableFactory[collection.Set] = LinkedHashSet
}

override def keySet: collection.Set[K] = new LinkedKeySet
override def keySet: collection.Set[K] = new MapOps.LazyKeySet(this) {
override def iterableFactory: IterableFactory[collection.Set] = LinkedHashSet
}

override def keysIterator: Iterator[K] =
if (size == 0) Iterator.empty
Expand Down
12 changes: 12 additions & 0 deletions project/MiMaFilters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@ object MiMaFilters {
// Additions that require a new minor version of the library
Build.mimaPreviousDottyVersion -> Seq(
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.caps.package#package.freeze"),

// against 3.8.0-RC1
// private[MapOps] MapOps.allKeys changing types
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.collection.MapOps#GenKeySet.allKeys"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.collection.MapOps#GenKeySet.scala$collection$MapOps$GenKeySet$_setter_$allKeys_="),
ProblemFilters.exclude[IncompatibleResultTypeProblem]("scala.collection.MapOps#KeySet.allKeys"),
ProblemFilters.exclude[IncompatibleResultTypeProblem]("scala.collection.SortedMapOps#KeySortedSet.allKeys"),
ProblemFilters.exclude[IncompatibleResultTypeProblem]("scala.collection.immutable.SortedMapOps#ImmutableKeySortedSet.allKeys"),
// new private[collection] classes
ProblemFilters.exclude[MissingClassProblem]("scala.collection.SortedMapOps$LazyKeySortedSet"),
ProblemFilters.exclude[MissingClassProblem]("scala.collection.immutable.MapOps$LazyImmutableKeySet"),
ProblemFilters.exclude[MissingClassProblem]("scala.collection.immutable.SortedMapOps$LazyImmutableKeySortedSet"),
),

)
Expand Down
Loading