From dd00323b5b93f43ef75cbb1b735d3da52e4fb7c8 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 7 Apr 2024 17:27:03 -0700 Subject: [PATCH 1/3] Cross-build and tweak syntax --- build.sbt | 21 +++++++++-- project/build.properties | 2 +- .../scala/scala/collection/MultiDict.scala | 17 ++++----- .../scala/scala/collection/MultiSet.scala | 19 +++++----- .../scala/collection/SortedMultiDict.scala | 6 ++-- .../scala/collection/SortedMultiSet.scala | 6 ++-- .../decorators/BitSetDecorator.scala | 2 +- .../decorators/IteratorDecorator.scala | 35 ++++++++----------- .../scala/collection/decorators/package.scala | 2 +- .../scala/collection/decorators/views.scala | 4 +-- .../scala/collection/mutable/MultiDict.scala | 15 ++++---- .../scala/collection/mutable/MultiSet.scala | 6 ++-- .../collection/mutable/SortedMultiDict.scala | 6 ++-- .../collection/mutable/SortedMultiSet.scala | 6 ++-- 14 files changed, 75 insertions(+), 72 deletions(-) diff --git a/build.sbt b/build.sbt index 59a1264..e20c033 100644 --- a/build.sbt +++ b/build.sbt @@ -1,5 +1,5 @@ ThisBuild / scalaVersion := "3.3.3" -ThisBuild / crossScalaVersions := Seq((ThisBuild / scalaVersion).value, "2.13.13") +ThisBuild / crossScalaVersions := Seq((ThisBuild / scalaVersion).value, "2.13.14") lazy val root = project.in(file(".")) .aggregate(collectionContrib.jvm, collectionContrib.js, collectionContrib.native) @@ -20,8 +20,23 @@ lazy val collectionContrib = crossProject(JVMPlatform, JSPlatform, NativePlatfor scalaModuleAutomaticModuleName := Some("scala.collection.contrib"), Compile / compile / scalacOptions ++= { CrossVersion.partialVersion(scalaVersion.value) match { - case Some((2, _)) => Seq("-opt-warnings", "-Werror", "-Wconf:origin=scala.collection.IterableOps.toIterable:s") - case _ => Seq("-Xfatal-warnings", "-Wconf:cat=deprecation:s") + case Some((2, _)) => Seq( + "-Wconf:origin=scala.collection.IterableOps.toIterable:s", // internal usage; annotating @nowarn is clutter + "-Werror", + "-Wnonunit-statement", + "-Wopt", + "-opt:inline:", + "-opt:inline:scala.util.package$chaining$,scala.util.ChainingSyntax,scala.util.ChainingOps$", + "-Wvalue-discard", + "-Xlint", + "-Xsource:3-cross", + ) + case _ => Seq( + "-Wconf:cat=deprecation:s", // Scala 3 lacks origin, src arrives in 3.3.4 & 3.5 + "-Wconf:id=E175:s", + "-Werror", + "-Wnonunit-statement", // warns on uni-limbed if + ) } }, Compile / doc / scalacOptions ++= { diff --git a/project/build.properties b/project/build.properties index 04267b1..081fdbb 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.9.9 +sbt.version=1.10.0 diff --git a/src/main/scala/scala/collection/MultiDict.scala b/src/main/scala/scala/collection/MultiDict.scala index 29cf304..7d254e5 100644 --- a/src/main/scala/scala/collection/MultiDict.scala +++ b/src/main/scala/scala/collection/MultiDict.scala @@ -13,7 +13,7 @@ trait MultiDict[K, V] with MultiDictOps[K, V, MultiDict, MultiDict[K, V]] with Equals { - override protected[this] def className: String = "MultiDict" + override protected def className: String = "MultiDict" def multiDictFactory: MapFactory[MultiDict] = MultiDict override protected def fromSpecific(coll: IterableOnce[(K, V)]): MultiDict[K, V] = multiDictFactory.from(coll) @@ -26,14 +26,11 @@ trait MultiDict[K, V] override def equals(o: Any): Boolean = o match { case that: MultiDict[K @unchecked, _] => (this eq that) || - (that canEqual this) && - (this.size == that.size) && { - try { - sets forall { case (k, vs) => that.sets.get(k).contains(vs) } - } catch { - case _: ClassCastException => false - } - } + that.canEqual(this) && + this.size == that.size && ( + try sets.forall { case (k, vs) => that.sets.get(k).contains(vs) } + catch { case _: ClassCastException => false } + ) case _ => false } @@ -205,7 +202,7 @@ trait MultiDictOps[K, V, +CC[X, Y] <: MultiDict[X, Y], +C <: MultiDict[K, V]] object MultiDictOps { class WithFilter[K, V, +IterableCC[_], +CC[X, Y] <: MultiDict[X, Y]]( - `this`: MultiDictOps[K, V, CC, _] with IterableOps[(K, V), IterableCC, _], + `this`: MultiDictOps[K, V, CC, ?] & IterableOps[(K, V), IterableCC, ?], p: ((K, V)) => Boolean ) extends IterableOps.WithFilter[(K, V), IterableCC](`this`, p) { diff --git a/src/main/scala/scala/collection/MultiSet.scala b/src/main/scala/scala/collection/MultiSet.scala index 0465c8b..fc50026 100644 --- a/src/main/scala/scala/collection/MultiSet.scala +++ b/src/main/scala/scala/collection/MultiSet.scala @@ -12,7 +12,7 @@ trait MultiSet[A] with MultiSetOps[A, MultiSet, MultiSet[A]] with Equals { - override protected[this] def className: String = "MultiSet" + override protected def className: String = "MultiSet" override def iterableFactory: IterableFactory[MultiSet] = MultiSet override protected def fromSpecific(coll: IterableOnce[A]): MultiSet[A] = iterableFactory.from(coll) @@ -24,14 +24,11 @@ trait MultiSet[A] override def equals(o: Any): Boolean = o match { case that: MultiSet[A @unchecked] => (this eq that) || - (that canEqual this) && - (this.size == that.size) && { - try { - occurrences forall { case (elem, n) => that.get(elem) == n } - } catch { - case _: ClassCastException => false - } - } + that.canEqual(this) && + this.size == that.size && ( + try occurrences.forall { case (elem, n) => that.get(elem) == n } + catch { case _: ClassCastException => false } + ) case _ => false } @@ -42,10 +39,10 @@ trait MultiSet[A] trait MultiSetOps[A, +CC[X] <: MultiSet[X], +C <: MultiSet[A]] extends IterableOps[A, CC, C] { - protected[this] def fromSpecificOccurrences(it: Iterable[(A, Int)]): C = + protected def fromSpecificOccurrences(it: Iterable[(A, Int)]): C = fromSpecific(it.view.flatMap { case (e, n) => new View.Fill(n)(e) }) - protected[this] def fromOccurrences[E](it: Iterable[(E, Int)]): CC[E] = + protected def fromOccurrences[E](it: Iterable[(E, Int)]): CC[E] = // Note new MultiSet(it.to(Map)) would be more efficient but would also loose duplicates iterableFactory.from(it.view.flatMap { case (e, n) => new View.Fill(n)(e) }) diff --git a/src/main/scala/scala/collection/SortedMultiDict.scala b/src/main/scala/scala/collection/SortedMultiDict.scala index 9351cc6..d6afee1 100644 --- a/src/main/scala/scala/collection/SortedMultiDict.scala +++ b/src/main/scala/scala/collection/SortedMultiDict.scala @@ -25,8 +25,8 @@ trait SortedMultiDictOps[K, V, +CC[X, Y] <: MultiDict[X, Y], +C <: MultiDict[K, def sortedMultiDictFactory: SortedMapFactory[CC] - protected[this] def sortedFromIterable[L : Ordering, W](it: Iterable[(L, W)]): CC[L, W] = sortedMultiDictFactory.from(it) - protected[this] def sortedFromSets[L : Ordering, W](it: Iterable[(L, Set[W])]): CC[L, W] = + protected def sortedFromIterable[L : Ordering, W](it: Iterable[(L, W)]): CC[L, W] = sortedMultiDictFactory.from(it) + protected def sortedFromSets[L : Ordering, W](it: Iterable[(L, Set[W])]): CC[L, W] = sortedFromIterable(it.view.flatMap { case (l, ws) => ws.map(w => (l, w)) }) /** `this` sorted multidict upcasted to an unsorted multidict */ @@ -126,7 +126,7 @@ trait SortedMultiDictOps[K, V, +CC[X, Y] <: MultiDict[X, Y], +C <: MultiDict[K, object SortedMultiDictOps { class WithFilter[K, V, +IterableCC[_], +MultiDictCC[X, Y] <: MultiDict[X, Y], +CC[X, Y] <: MultiDict[X, Y]]( - `this`: SortedMultiDictOps[K, V, CC, _] with MultiDictOps[K, V, MultiDictCC, _] with IterableOps[(K, V), IterableCC, _], + `this`: SortedMultiDictOps[K, V, CC, ?] & MultiDictOps[K, V, MultiDictCC, ?] & IterableOps[(K, V), IterableCC, ?], p: ((K, V)) => Boolean ) extends MultiDictOps.WithFilter[K, V, IterableCC, MultiDictCC](`this`, p) { diff --git a/src/main/scala/scala/collection/SortedMultiSet.scala b/src/main/scala/scala/collection/SortedMultiSet.scala index 0e532f7..3fd2613 100644 --- a/src/main/scala/scala/collection/SortedMultiSet.scala +++ b/src/main/scala/scala/collection/SortedMultiSet.scala @@ -118,7 +118,7 @@ trait SortedMultiSetOps[A, +CC[X] <: MultiSet[X], +C <: MultiSet[A]] * is the minimum of the lengths of `this` and `that` */ def zip[B](that: Iterable[B])(implicit ev: Ordering[B]): CC[(A @uncheckedVariance, B)] = // sound bcs of VarianceNote - sortedFromIterable(new View.Zip(toIterable, that))(Ordering.Tuple2(ordering, implicitly)) + sortedFromIterable(new View.Zip(toIterable, that))(using Ordering.Tuple2(ordering, implicitly)) /** * @return a new collection resulting from applying the given partial @@ -147,7 +147,7 @@ trait SortedMultiSetOps[A, +CC[X] <: MultiSet[X], +C <: MultiSet[A]] // --- Override return type of methods that returned an unsorted MultiSet override def zipWithIndex: CC[(A, Int)] = - sortedFromIterable(new View.ZipWithIndex(toIterable))(Ordering.Tuple2(ordering, implicitly)) + sortedFromIterable(new View.ZipWithIndex(toIterable))(using Ordering.Tuple2(ordering, implicitly)) } @@ -158,7 +158,7 @@ object SortedMultiSetOps { * @define coll sorted collection */ class WithFilter[A, +IterableCC[_], +CC[X] <: MultiSet[X]]( - `this`: SortedMultiSetOps[A, CC, _] with IterableOps[A, IterableCC, _], + `this`: SortedMultiSetOps[A, CC, ?] & IterableOps[A, IterableCC, ?], p: A => Boolean ) extends IterableOps.WithFilter[A, IterableCC](`this`, p) { diff --git a/src/main/scala/scala/collection/decorators/BitSetDecorator.scala b/src/main/scala/scala/collection/decorators/BitSetDecorator.scala index 0b5e294..663fcc5 100644 --- a/src/main/scala/scala/collection/decorators/BitSetDecorator.scala +++ b/src/main/scala/scala/collection/decorators/BitSetDecorator.scala @@ -2,7 +2,7 @@ package scala.collection.decorators import scala.collection.{BitSet, BitSetOps} -class BitSetDecorator[+C <: BitSet with BitSetOps[C]](protected val bs: C) { +class BitSetDecorator[+C <: BitSet & BitSetOps[C]](protected val bs: C) { import BitSetDecorator._ import BitSetOps._ diff --git a/src/main/scala/scala/collection/decorators/IteratorDecorator.scala b/src/main/scala/scala/collection/decorators/IteratorDecorator.scala index 79aa4bf..c4367ba 100644 --- a/src/main/scala/scala/collection/decorators/IteratorDecorator.scala +++ b/src/main/scala/scala/collection/decorators/IteratorDecorator.scala @@ -2,6 +2,7 @@ package scala.collection package decorators import scala.annotation.tailrec +import scala.util.chaining.* import scala.util.control.NonFatal /** Enriches Iterator with additional methods. @@ -188,8 +189,8 @@ class IteratorDecorator[A](val `this`: Iterator[A]) extends AnyVal { */ def splitBy[K](f: A => K): Iterator[immutable.Seq[A]] = new AbstractIterator[immutable.Seq[A]] { - private var hd: A = _ - private var hdKey: K = _ + private var hd: A = null.asInstanceOf[A] // todo uninitialized + private var hdKey: K = null.asInstanceOf[K] // todo uninitialized private var hdDefined: Boolean = false override def hasNext: Boolean = hdDefined || `this`.hasNext @@ -240,32 +241,26 @@ class IteratorDecorator[A](val `this`: Iterator[A]) extends AnyVal { } /** Gives elements from the source iterator until the source iterator ends or throws a NonFatal exception. - * - * @param exceptionCaught a callback invoked from `hasNext` when the source iterator throws a NonFatal exception - * @return an iterator that takes items until the wrapped iterator ends or throws a NonFatal exception - * @see scala.util.control.NonFatal - * @note Reuse: $consumesAndProducesIterator - */ - def takeUntilException(exceptionCaught: Throwable => Unit): Iterator[A] = { + * + * @param exceptionCaught a callback invoked from `hasNext` when the source iterator throws a NonFatal exception + * @return an iterator that takes items until the wrapped iterator ends or throws a NonFatal exception + * @see scala.util.control.NonFatal + * @note Reuse: $consumesAndProducesIterator + */ + def takeUntilException(exceptionCaught: Throwable => Unit): Iterator[A] = new AbstractIterator[A] { private val wrapped = `this`.buffered - override def hasNext: Boolean = { - try { - val n = wrapped.hasNext - // By already invoking `head` (and therefore also `next` on `this`), - // we are sure that `wrapped.next` will not throw when it is used from - // `next`. - if (n) wrapped.head - n - } catch { + override def hasNext: Boolean = + // By already invoking `head` (and therefore also `next` on `this`), + // we are sure that `wrapped.next` will not throw when it is used from `next`. + try wrapped.hasNext.tap(if(_) wrapped.head) + catch { case NonFatal(t) => exceptionCaught(t) false } - } override def next(): A = wrapped.next() } - } } diff --git a/src/main/scala/scala/collection/decorators/package.scala b/src/main/scala/scala/collection/decorators/package.scala index e13ccb9..3175d88 100644 --- a/src/main/scala/scala/collection/decorators/package.scala +++ b/src/main/scala/scala/collection/decorators/package.scala @@ -17,7 +17,7 @@ package object decorators { implicit def mapDecorator[C](coll: C)(implicit map: IsMap[C]): MapDecorator[C, map.type] = new MapDecorator(coll)(map) - implicit def bitSetDecorator[C <: BitSet with BitSetOps[C]](bs: C): BitSetDecorator[C] = + implicit def bitSetDecorator[C <: BitSet & BitSetOps[C]](bs: C): BitSetDecorator[C] = new BitSetDecorator(bs) implicit def mutableBitSetDecorator(bs: mutable.BitSet): MutableBitSetDecorator = diff --git a/src/main/scala/scala/collection/decorators/views.scala b/src/main/scala/scala/collection/decorators/views.scala index 050b4a1..1666372 100644 --- a/src/main/scala/scala/collection/decorators/views.scala +++ b/src/main/scala/scala/collection/decorators/views.scala @@ -4,7 +4,7 @@ package decorators /** Views used by decorators */ object View { - type SomeIterableOps[+A] = IterableOps[A, AnyConstr, _] + type SomeIterableOps[+A] = IterableOps[A, AnyConstr, ?] class Intersperse[A](underlying: SomeIterableOps[A], sep: A) extends View[A] { def iterator: Iterator[A] = underlying.iterator.intersperse(sep) @@ -21,4 +21,4 @@ object View { else underlying.knownSize } -} \ No newline at end of file +} diff --git a/src/main/scala/scala/collection/mutable/MultiDict.scala b/src/main/scala/scala/collection/mutable/MultiDict.scala index c364538..d0eb42d 100644 --- a/src/main/scala/scala/collection/mutable/MultiDict.scala +++ b/src/main/scala/scala/collection/mutable/MultiDict.scala @@ -2,11 +2,10 @@ package scala package collection package mutable -/** - * A mutable multidict - * @tparam K the type of keys - * @tparam V the type of values - */ +/** A mutable multidict. + * @tparam K the type of keys + * @tparam V the type of values + */ class MultiDict[K, V] private (elems: Map[K, Set[V]]) extends collection.MultiDict[K, V] with Iterable[(K, V)] @@ -27,7 +26,7 @@ class MultiDict[K, V] private (elems: Map[K, Set[V]]) def addOne(elem: (K, V)): this.type = { val (k, v) = elem - elems.updateWith(k) { + val _ = elems.updateWith(k) { case None => Some(Set(v)) case Some(vs) => Some(vs += v) } @@ -36,7 +35,7 @@ class MultiDict[K, V] private (elems: Map[K, Set[V]]) def subtractOne(elem: (K, V)): this.type = { val (k, v) = elem - elems.updateWith(k) { + val _ = elems.updateWith(k) { case Some(vs) => vs -= v if (vs.nonEmpty) Some(vs) else None @@ -69,4 +68,4 @@ object MultiDict extends MapFactory[MultiDict] { def newBuilder[K, V]: Builder[(K, V), MultiDict[K, V]] = new GrowableBuilder[(K, V), MultiDict[K, V]](empty) -} \ No newline at end of file +} diff --git a/src/main/scala/scala/collection/mutable/MultiSet.scala b/src/main/scala/scala/collection/mutable/MultiSet.scala index a387e90..db90122 100644 --- a/src/main/scala/scala/collection/mutable/MultiSet.scala +++ b/src/main/scala/scala/collection/mutable/MultiSet.scala @@ -24,7 +24,7 @@ class MultiSetImpl[A] private[mutable] (val elems: Map[A, Int]) extends MultiSet def occurrences: collection.Map[A, Int] = elems def addOne(elem: A): this.type = { - elems.updateWith(elem) { + val _ = elems.updateWith(elem) { case None => Some(1) case Some(n) => Some(n + 1) } @@ -32,7 +32,7 @@ class MultiSetImpl[A] private[mutable] (val elems: Map[A, Int]) extends MultiSet } def subtractOne(elem: A): this.type = { - elems.updateWith(elem) { + val _ = elems.updateWith(elem) { case Some(n) => if (n > 1) Some(n - 1) else None case None => None } @@ -50,4 +50,4 @@ object MultiSet extends IterableFactory[MultiSet] { def newBuilder[A]: Builder[A, MultiSet[A]] = new GrowableBuilder[A, MultiSet[A]](empty) -} \ No newline at end of file +} diff --git a/src/main/scala/scala/collection/mutable/SortedMultiDict.scala b/src/main/scala/scala/collection/mutable/SortedMultiDict.scala index 1d4dcef..2c8e63b 100644 --- a/src/main/scala/scala/collection/mutable/SortedMultiDict.scala +++ b/src/main/scala/scala/collection/mutable/SortedMultiDict.scala @@ -31,7 +31,7 @@ class SortedMultiDict[K, V] private (elems: SortedMap[K, Set[V]])(implicit val o def addOne(elem: (K, V)): this.type = { val (k, v) = elem - elems.updateWith(k) { + val _ = elems.updateWith(k) { case None => Some(Set(v)) case Some(vs) => Some(vs += v) } @@ -40,7 +40,7 @@ class SortedMultiDict[K, V] private (elems: SortedMap[K, Set[V]])(implicit val o def subtractOne(elem: (K, V)): this.type = { val (k, v) = elem - elems.updateWith(k) { + val _ = elems.updateWith(k) { case Some(vs) => vs -= v if (vs.nonEmpty) Some(vs) else None @@ -76,4 +76,4 @@ object SortedMultiDict extends SortedMapFactory[SortedMultiDict] { def newBuilder[K: Ordering, V]: Builder[(K, V), SortedMultiDict[K, V]] = new GrowableBuilder[(K, V), SortedMultiDict[K, V]](empty) -} \ No newline at end of file +} diff --git a/src/main/scala/scala/collection/mutable/SortedMultiSet.scala b/src/main/scala/scala/collection/mutable/SortedMultiSet.scala index 1bc1667..4320504 100644 --- a/src/main/scala/scala/collection/mutable/SortedMultiSet.scala +++ b/src/main/scala/scala/collection/mutable/SortedMultiSet.scala @@ -28,7 +28,7 @@ class SortedMultiSet[A] private (elems: SortedMap[A, Int])(implicit val ordering new SortedMultiSet(elems.rangeImpl(from, until)) def addOne(elem: A): this.type = { - elems.updateWith(elem) { + val _ = elems.updateWith(elem) { case None => Some(1) case Some(n) => Some(n + 1) } @@ -36,7 +36,7 @@ class SortedMultiSet[A] private (elems: SortedMap[A, Int])(implicit val ordering } def subtractOne(elem: A): this.type = { - elems.updateWith(elem) { + val _ = elems.updateWith(elem) { case Some(n) => if (n > 1) Some(n - 1) else None case None => None } @@ -54,4 +54,4 @@ object SortedMultiSet extends SortedIterableFactory[SortedMultiSet] { def newBuilder[A: Ordering]: Builder[A, SortedMultiSet[A]] = new GrowableBuilder[A, SortedMultiSet[A]](empty) -} \ No newline at end of file +} From 13f29343ba0131318a07677aafebca0de2f9b0f2 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 24 May 2024 10:40:51 -0700 Subject: [PATCH 2/3] Improve addOne --- .../scala/collection/mutable/MultiDict.scala | 16 +++++++--------- .../scala/collection/mutable/MultiSet.scala | 15 +++++++-------- .../collection/mutable/SortedMultiDict.scala | 16 +++++++--------- .../collection/mutable/SortedMultiSet.scala | 17 ++++++++--------- 4 files changed, 29 insertions(+), 35 deletions(-) diff --git a/src/main/scala/scala/collection/mutable/MultiDict.scala b/src/main/scala/scala/collection/mutable/MultiDict.scala index d0eb42d..20b121b 100644 --- a/src/main/scala/scala/collection/mutable/MultiDict.scala +++ b/src/main/scala/scala/collection/mutable/MultiDict.scala @@ -26,20 +26,18 @@ class MultiDict[K, V] private (elems: Map[K, Set[V]]) def addOne(elem: (K, V)): this.type = { val (k, v) = elem - val _ = elems.updateWith(k) { - case None => Some(Set(v)) - case Some(vs) => Some(vs += v) - } + val vs = elems.getOrElseUpdate(k, Set.empty[V]) + vs.addOne(v) this } def subtractOne(elem: (K, V)): this.type = { val (k, v) = elem val _ = elems.updateWith(k) { - case Some(vs) => - vs -= v - if (vs.nonEmpty) Some(vs) else None - case None => None + case existing @ Some(vs) => + vs.subtractOne(v) + if (vs.isEmpty) None else existing + case _ => None } this } @@ -49,7 +47,7 @@ class MultiDict[K, V] private (elems: Map[K, Set[V]]) * @return the collection itself */ def removeKey(key: K): this.type = { - elems -= key + elems.subtractOne(key) this } diff --git a/src/main/scala/scala/collection/mutable/MultiSet.scala b/src/main/scala/scala/collection/mutable/MultiSet.scala index db90122..7dfd30c 100644 --- a/src/main/scala/scala/collection/mutable/MultiSet.scala +++ b/src/main/scala/scala/collection/mutable/MultiSet.scala @@ -2,6 +2,8 @@ package scala package collection package mutable +import java.util.concurrent.atomic.AtomicInteger + /** * A mutable multiset. */ @@ -19,22 +21,19 @@ trait MultiSet[A] override def knownSize: Int = super[Growable].knownSize } -class MultiSetImpl[A] private[mutable] (val elems: Map[A, Int]) extends MultiSet[A] { +class MultiSetImpl[A] private[mutable] (elems: Map[A, AtomicInteger]) extends MultiSet[A] { - def occurrences: collection.Map[A, Int] = elems + def occurrences: collection.Map[A, Int] = elems.map { case (k, v) => (k, v.get) } def addOne(elem: A): this.type = { - val _ = elems.updateWith(elem) { - case None => Some(1) - case Some(n) => Some(n + 1) - } + elems.getOrElseUpdate(elem, new AtomicInteger).getAndIncrement this } def subtractOne(elem: A): this.type = { val _ = elems.updateWith(elem) { - case Some(n) => if (n > 1) Some(n - 1) else None - case None => None + case existing @ Some(n) => if (n.decrementAndGet <= 0) None else existing + case _ => None } this } diff --git a/src/main/scala/scala/collection/mutable/SortedMultiDict.scala b/src/main/scala/scala/collection/mutable/SortedMultiDict.scala index 2c8e63b..7bcf347 100644 --- a/src/main/scala/scala/collection/mutable/SortedMultiDict.scala +++ b/src/main/scala/scala/collection/mutable/SortedMultiDict.scala @@ -31,20 +31,18 @@ class SortedMultiDict[K, V] private (elems: SortedMap[K, Set[V]])(implicit val o def addOne(elem: (K, V)): this.type = { val (k, v) = elem - val _ = elems.updateWith(k) { - case None => Some(Set(v)) - case Some(vs) => Some(vs += v) - } + val vs = elems.getOrElseUpdate(k, Set.empty[V]) + vs.addOne(v) this } def subtractOne(elem: (K, V)): this.type = { val (k, v) = elem val _ = elems.updateWith(k) { - case Some(vs) => - vs -= v - if (vs.nonEmpty) Some(vs) else None - case None => None + case existing @ Some(vs) => + vs.subtractOne(v) + if (vs.isEmpty) None else existing + case _ => None } this } @@ -54,7 +52,7 @@ class SortedMultiDict[K, V] private (elems: SortedMap[K, Set[V]])(implicit val o * @return the collection itself */ def removeKey(key: K): this.type = { - elems -= key + elems.subtractOne(key) this } diff --git a/src/main/scala/scala/collection/mutable/SortedMultiSet.scala b/src/main/scala/scala/collection/mutable/SortedMultiSet.scala index 4320504..a4d5622 100644 --- a/src/main/scala/scala/collection/mutable/SortedMultiSet.scala +++ b/src/main/scala/scala/collection/mutable/SortedMultiSet.scala @@ -2,12 +2,14 @@ package scala package collection package mutable +import java.util.concurrent.atomic.AtomicInteger + /** * A mutable multiset whose elements are sorted according to a given ordering. * * @tparam A Type of elements */ -class SortedMultiSet[A] private (elems: SortedMap[A, Int])(implicit val ordering: Ordering[A]) +class SortedMultiSet[A] private (elems: SortedMap[A, AtomicInteger])(implicit val ordering: Ordering[A]) extends MultiSet[A] with collection.SortedMultiSet[A] with collection.SortedMultiSetOps[A, SortedMultiSet, SortedMultiSet[A]] @@ -15,7 +17,7 @@ class SortedMultiSet[A] private (elems: SortedMap[A, Int])(implicit val ordering with Growable[A] with Shrinkable[A] { - def occurrences: collection.SortedMap[A, Int] = elems + def occurrences: collection.SortedMap[A, Int] = elems.map { case (k, v) => (k, v.get) } override def sortedIterableFactory: SortedIterableFactory[SortedMultiSet] = SortedMultiSet override protected def fromSpecific(coll: IterableOnce[A]): SortedMultiSet[A] = sortedIterableFactory.from(coll) @@ -28,17 +30,14 @@ class SortedMultiSet[A] private (elems: SortedMap[A, Int])(implicit val ordering new SortedMultiSet(elems.rangeImpl(from, until)) def addOne(elem: A): this.type = { - val _ = elems.updateWith(elem) { - case None => Some(1) - case Some(n) => Some(n + 1) - } + elems.getOrElseUpdate(elem, new AtomicInteger).getAndIncrement this } def subtractOne(elem: A): this.type = { val _ = elems.updateWith(elem) { - case Some(n) => if (n > 1) Some(n - 1) else None - case None => None + case existing @ Some(n) => if (n.decrementAndGet <= 0) None else existing + case _ => None } this } @@ -50,7 +49,7 @@ object SortedMultiSet extends SortedIterableFactory[SortedMultiSet] { def from[E: Ordering](it: IterableOnce[E]): SortedMultiSet[E] = (newBuilder[E] ++= it).result() - def empty[A: Ordering]: SortedMultiSet[A] = new SortedMultiSet[A](SortedMap.empty[A, Int]) + def empty[A: Ordering]: SortedMultiSet[A] = new SortedMultiSet[A](SortedMap.empty[A, AtomicInteger]) def newBuilder[A: Ordering]: Builder[A, SortedMultiSet[A]] = new GrowableBuilder[A, SortedMultiSet[A]](empty) From 8e84fd343c3655447ae373c1c86f8777ac50aa51 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 24 May 2024 11:21:57 -0700 Subject: [PATCH 3/3] Avoid updateWith These mutable structures update rarely: normally they mutate. Since updateWith is for updating, just get the thing to mutate. Rarely, when the thing is empty, remove the key. --- src/main/scala/scala/collection/mutable/MultiDict.scala | 8 ++++---- src/main/scala/scala/collection/mutable/MultiSet.scala | 6 +++--- .../scala/scala/collection/mutable/SortedMultiDict.scala | 8 ++++---- .../scala/scala/collection/mutable/SortedMultiSet.scala | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/scala/scala/collection/mutable/MultiDict.scala b/src/main/scala/scala/collection/mutable/MultiDict.scala index 20b121b..c7190b0 100644 --- a/src/main/scala/scala/collection/mutable/MultiDict.scala +++ b/src/main/scala/scala/collection/mutable/MultiDict.scala @@ -33,11 +33,11 @@ class MultiDict[K, V] private (elems: Map[K, Set[V]]) def subtractOne(elem: (K, V)): this.type = { val (k, v) = elem - val _ = elems.updateWith(k) { - case existing @ Some(vs) => + elems.get(k) match { + case Some(vs) => vs.subtractOne(v) - if (vs.isEmpty) None else existing - case _ => None + if (vs.isEmpty) elems.subtractOne(k) + case _ => } this } diff --git a/src/main/scala/scala/collection/mutable/MultiSet.scala b/src/main/scala/scala/collection/mutable/MultiSet.scala index 7dfd30c..1c344da 100644 --- a/src/main/scala/scala/collection/mutable/MultiSet.scala +++ b/src/main/scala/scala/collection/mutable/MultiSet.scala @@ -31,9 +31,9 @@ class MultiSetImpl[A] private[mutable] (elems: Map[A, AtomicInteger]) extends Mu } def subtractOne(elem: A): this.type = { - val _ = elems.updateWith(elem) { - case existing @ Some(n) => if (n.decrementAndGet <= 0) None else existing - case _ => None + elems.get(elem) match { + case Some(n) => if (n.decrementAndGet <= 0) elems.subtractOne(elem) + case _ => } this } diff --git a/src/main/scala/scala/collection/mutable/SortedMultiDict.scala b/src/main/scala/scala/collection/mutable/SortedMultiDict.scala index 7bcf347..e5ff3ae 100644 --- a/src/main/scala/scala/collection/mutable/SortedMultiDict.scala +++ b/src/main/scala/scala/collection/mutable/SortedMultiDict.scala @@ -38,11 +38,11 @@ class SortedMultiDict[K, V] private (elems: SortedMap[K, Set[V]])(implicit val o def subtractOne(elem: (K, V)): this.type = { val (k, v) = elem - val _ = elems.updateWith(k) { - case existing @ Some(vs) => + elems.get(k) match { + case Some(vs) => vs.subtractOne(v) - if (vs.isEmpty) None else existing - case _ => None + if (vs.isEmpty) elems.subtractOne(k) + case _ => } this } diff --git a/src/main/scala/scala/collection/mutable/SortedMultiSet.scala b/src/main/scala/scala/collection/mutable/SortedMultiSet.scala index a4d5622..525902b 100644 --- a/src/main/scala/scala/collection/mutable/SortedMultiSet.scala +++ b/src/main/scala/scala/collection/mutable/SortedMultiSet.scala @@ -35,9 +35,9 @@ class SortedMultiSet[A] private (elems: SortedMap[A, AtomicInteger])(implicit va } def subtractOne(elem: A): this.type = { - val _ = elems.updateWith(elem) { - case existing @ Some(n) => if (n.decrementAndGet <= 0) None else existing - case _ => None + elems.get(elem) match { + case Some(n) => if (n.decrementAndGet <= 0) elems.subtractOne(elem) + case _ => } this }