Skip to content

Commit

Permalink
Factor out strict implementations of transformation operations.
Browse files Browse the repository at this point in the history
Add StrictOptimizedMapOps and StrictOptimizedSortedMapOps.

Only operations that were implemented more than once have been factored
out. Some other operations could be factored out so that they could
be reused by custom collection implementations.

Fixes scala/collection-strawman#325
  • Loading branch information
julienrf committed Jun 22, 2018
1 parent 5450358 commit 2e37a99
Show file tree
Hide file tree
Showing 20 changed files with 268 additions and 129 deletions.
4 changes: 3 additions & 1 deletion src/library/scala/collection/Seq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,9 @@ trait SeqOps[+A, +CC[_], +C] extends Any

// Make `concat` an alias for `appendedAll` so that it benefits from performance
// overrides of this method
@`inline` final override def concat[B >: A](suffix: Iterable[B]): CC[B] = appendedAll(suffix)
// TODO https://github.com/scala/bug/issues/10853 Uncomment final
@deprecatedOverriding("This method should be final, but is not due to scala/bug#10853", "2.13.0")
@`inline` /*final*/ override def concat[B >: A](suffix: Iterable[B]): CC[B] = appendedAll(suffix)

/** Produces a new sequence which contains all elements of this $coll and also all elements of
* a given sequence. `xs union ys` is equivalent to `xs ++ ys`.
Expand Down
2 changes: 1 addition & 1 deletion src/library/scala/collection/SortedMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ trait SortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _],
}

object SortedMapOps {
private final val ordMsg = "No implicit Ordering[${K2}] found to build a SortedMap[${K2}, ${V2}]. You may want to upcast to a Map[${K}, ${V}] first by calling `unsorted`."
private[collection] final val ordMsg = "No implicit Ordering[${K2}] found to build a SortedMap[${K2}, ${V2}]. You may want to upcast to a Map[${K}, ${V}] first by calling `unsorted`."

/** Specializes `MapWithFilter` for sorted Map collections
*
Expand Down
60 changes: 15 additions & 45 deletions src/library/scala/collection/StrictOptimizedIterableOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import scala.annotation.unchecked.uncheckedVariance
import scala.language.higherKinds

/**
* Trait that overrides operations to take advantage of strict builders.
* Trait that overrides iterable operations to take advantage of strict builders.
*
* @tparam A Elements type
* @tparam CC Collection type constructor
* @tparam C Collection type
*/
trait StrictOptimizedIterableOps[+A, +CC[_], +C]
Expand Down Expand Up @@ -70,54 +71,23 @@ trait StrictOptimizedIterableOps[+A, +CC[_], +C]
// the view-based implementations, but they turn out to be slightly faster because
// a couple of indirection levels are removed

override def map[B](f: A => B): CC[B] = {
val b = iterableFactory.newBuilder[B]
val it = iterator
while (it.hasNext) {
b += f(it.next())
}
b.result()
}
override def map[B](f: A => B): CC[B] =
StrictOptimizedOps.map(iterator, iterableFactory.newBuilder, f)

override def flatMap[B](f: A => IterableOnce[B]): CC[B] = {
val b = iterableFactory.newBuilder[B]
val it = iterator
while (it.hasNext) {
b ++= f(it.next())
}
b.result()
}
override def flatMap[B](f: A => IterableOnce[B]): CC[B] =
StrictOptimizedOps.flatMap(iterator, iterableFactory.newBuilder, f)

override def collect[B](pf: PartialFunction[A, B]): CC[B] = {
val b = iterableFactory.newBuilder[B]
val it = iterator
while (it.hasNext) {
val elem = it.next()
if (pf.isDefinedAt(elem)) {
b += pf.apply(elem)
}
}
b.result()
}
override def concat[B >: A](suffix: Iterable[B]): CC[B] =
StrictOptimizedOps.concat(iterator, suffix.iterator, iterableFactory.newBuilder[B])

override def flatten[B](implicit toIterableOnce: A => IterableOnce[B]): CC[B] = {
val b = iterableFactory.newBuilder[B]
val it = iterator
while (it.hasNext) {
b ++= toIterableOnce(it.next())
}
b.result()
}
override def collect[B](pf: PartialFunction[A, B]): CC[B] =
StrictOptimizedOps.collect(iterator, iterableFactory.newBuilder, pf)

override def zip[B](that: Iterable[B]): CC[(A @uncheckedVariance, B)] = {
val b = iterableFactory.newBuilder[(A, B)]
val it1 = iterator
val it2 = that.iterator
while (it1.hasNext && it2.hasNext) {
b += ((it1.next(), it2.next()))
}
b.result()
}
override def flatten[B](implicit toIterableOnce: A => IterableOnce[B]): CC[B] =
StrictOptimizedOps.flatten(iterator, iterableFactory.newBuilder)

override def zip[B](that: Iterable[B]): CC[(A @uncheckedVariance, B)] =
StrictOptimizedOps.zip(iterator, that.iterator, iterableFactory.newBuilder[(A, B)])

override def zipWithIndex: CC[(A @uncheckedVariance, Int)] = {
val b = iterableFactory.newBuilder[(A, Int)]
Expand Down
29 changes: 29 additions & 0 deletions src/library/scala/collection/StrictOptimizedMapOps.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package scala.collection

import scala.language.higherKinds

/**
* Trait that overrides map operations to take advantage of strict builders.
*
* @tparam K Type of keys
* @tparam V Type of values
* @tparam CC Collection type constructor
* @tparam C Collection type
*/
trait StrictOptimizedMapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C]
extends MapOps[K, V, CC, C]
with StrictOptimizedIterableOps[(K, V), Iterable, C] {

override def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] =
StrictOptimizedOps.map(iterator, mapFactory.newBuilder, f)

override def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): CC[K2, V2] =
StrictOptimizedOps.flatMap(iterator, mapFactory.newBuilder, f)

override def concat[V2 >: V](suffix: Iterable[(K, V2)]): CC[K, V2] =
StrictOptimizedOps.concat(iterator, suffix.iterator, mapFactory.newBuilder)

override def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]): CC[K2, V2] =
StrictOptimizedOps.collect(iterator, mapFactory.newBuilder, pf)

}
109 changes: 109 additions & 0 deletions src/library/scala/collection/StrictOptimizedOps.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package scala.collection

/**
* Convenient implementations of transformation operations
* based on builders.
*
* All the methods defined here are expected to be called with
* freshly created iterators and builders.
*/
object StrictOptimizedOps {

/**
* @param it Iterator to map elements from
* @param b Builder to use to build the resulting collection
* @param f Element transformation function
* @tparam A Type of elements of the source collection (e.g. `Int`)
* @tparam B Type of elements of the resulting collection (e.g. `String`)
* @tparam C Type of the resulting collection (e.g. `List[String]`)
* @return The resulting collection
*/
@inline def map[A, B, C](it: Iterator[A], b: mutable.Builder[B, C], f: A => B): C = {
while (it.hasNext) {
b += f(it.next())
}
b.result()
}

/**
* @param it Iterator to flatMap elements from
* @param b Builder to use to build the resulting collection
* @param f Element transformation function
* @tparam A Type of elements of the source collection (e.g. `Int`)
* @tparam B Type of elements of the resulting collection (e.g. `String`)
* @tparam C Type of the resulting collection (e.g. `List[String]`)
* @return The resulting collection
*/
@inline def flatMap[A, B, C](it: Iterator[A], b: mutable.Builder[B, C], f: A => IterableOnce[B]): C = {
while (it.hasNext) {
b ++= f(it.next())
}
b.result()
}

/**
* @param it1 Iterator of the first collection
* @param it2 Iterator of the second collection
* @param b Builder to use to build the resulting collection
* @tparam A Type of elements (e.g. `Int`)
* @tparam C Type of the resulting collection (e.g. `List[Int]`)
* @return The resulting collection
*/
@inline def concat[A, C](it1: Iterator[A], it2: Iterator[A], b: mutable.Builder[A, C]): C = {
b ++= it1
b ++= it2
b.result()
}

/**
* @param it Iterator to collect elements from
* @param b Builder to use to build the resulting collection
* @param pf Element transformation partial function
* @tparam A Type of elements of the source collection (e.g. `Int`)
* @tparam B Type of elements of the resulting collection (e.g. `String`)
* @tparam C Type of the resulting collection (e.g. `List[String]`)
* @return The resulting collection
*/
@inline def collect[A, B, C](it: Iterator[A], b: mutable.Builder[B, C], pf: PartialFunction[A, B]): C = {
while (it.hasNext) {
val elem = it.next()
if (pf.isDefinedAt(elem)) {
b += pf.apply(elem)
}
}
b.result()
}

/**
* @param it Iterator to flatten elements from
* @param b Builder to use to build the resulting collection
* @param toIterableOnce Evidence that `A` can be seen as an `IterableOnce[B]`
* @tparam A Type of elements of the source collection (e.g. `List[Int]`)
* @tparam B Type of elements of the resulting collection (e.g. `Int`)
* @tparam C Type of the resulting collection (e.g. `List[Int]`)
* @return The resulting collection
*/
@inline def flatten[A, B, C](it: Iterator[A], b: mutable.Builder[B, C])(implicit toIterableOnce: A => IterableOnce[B]): C = {
while (it.hasNext) {
b ++= toIterableOnce(it.next())
}
b.result()
}

/**
* @param it1 Iterator of the first collection
* @param it2 Iterator of the second collection
* @param b Builder to use to build the resulting collection
* @tparam A Type of elements of the first collection (e.g. `Int`)
* @tparam B Type of elements of the second collection (e.g. `String`)
* @tparam C Type of the resulting collection (e.g. `List[(Int, String)]`)
* @return The resulting collection
*/
@inline def zip[A, B, C](it1: Iterator[A], it2: Iterator[B], b: mutable.Builder[(A, B), C]): C = {
while (it1.hasNext && it2.hasNext) {
b += ((it1.next(), it2.next()))
}
b.result()
}

}
12 changes: 4 additions & 8 deletions src/library/scala/collection/StrictOptimizedSeqOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import scala.language.higherKinds
*/
trait StrictOptimizedSeqOps [+A, +CC[_], +C]
extends Any
with SeqOps[A, CC, C]
with StrictOptimizedIterableOps[A, CC, C] {
with StrictOptimizedIterableOps[A, CC, C]
with SeqOps[A, CC, C] {

override def distinctBy[B](f: A => B): C = {
val builder = newSpecificBuilder
Expand Down Expand Up @@ -45,12 +45,8 @@ trait StrictOptimizedSeqOps [+A, +CC[_], +C]
b.result()
}

override def appendedAll[B >: A](suffix: Iterable[B]): CC[B] = {
val b = iterableFactory.newBuilder[B]
b ++= this
b ++= suffix
b.result()
}
override def appendedAll[B >: A](suffix: Iterable[B]): CC[B] =
StrictOptimizedOps.concat(iterator, suffix.iterator, iterableFactory.newBuilder)

override def prependedAll[B >: A](prefix: Iterable[B]): CC[B] = {
val b = iterableFactory.newBuilder[B]
Expand Down
17 changes: 17 additions & 0 deletions src/library/scala/collection/StrictOptimizedSetOps.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package scala.collection

/**
* Trait that overrides set operations to take advantage of strict builders.
*
* @tparam A Elements type
* @tparam CC Collection type constructor
* @tparam C Collection type
*/
trait StrictOptimizedSetOps[A, +CC[_], +C <: SetOps[A, CC, C]]
extends SetOps[A, CC, C]
with StrictOptimizedIterableOps[A, CC, C] {

override def concat(that: Iterable[A]): C =
StrictOptimizedOps.concat(iterator, that.iterator, newSpecificBuilder)

}
29 changes: 29 additions & 0 deletions src/library/scala/collection/StrictOptimizedSortedMapOps.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package scala.collection

import scala.annotation.implicitNotFound

/**
* Trait that overrides sorted map operations to take advantage of strict builders.
*
* @tparam K Type of keys
* @tparam V Type of values
* @tparam CC Collection type constructor
* @tparam C Collection type
*/
trait StrictOptimizedSortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _], +C <: SortedMapOps[K, V, CC, C]]
extends SortedMapOps[K, V, CC, C]
with StrictOptimizedMapOps[K, V, Map, C] {

override def map[K2, V2](f: ((K, V)) => (K2, V2))(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] =
StrictOptimizedOps.map(iterator, sortedMapFactory.newBuilder, f)

override def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)])(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] =
StrictOptimizedOps.flatMap(iterator, sortedMapFactory.newBuilder, f)

override def concat[V2 >: V](xs: Iterable[(K, V2)]): CC[K, V2] =
StrictOptimizedOps.concat(iterator, xs.iterator, sortedMapFactory.newBuilder)

override def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)])(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] =
StrictOptimizedOps.collect(iterator, sortedMapFactory.newBuilder, pf)

}
61 changes: 20 additions & 41 deletions src/library/scala/collection/StrictOptimizedSortedSetOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,48 +6,27 @@ import scala.annotation.implicitNotFound
import scala.annotation.unchecked.uncheckedVariance
import scala.language.higherKinds

/**
* Trait that overrides sorted set operations to take advantage of strict builders.
*
* @tparam A Elements type
* @tparam CC Collection type constructor
* @tparam C Collection type
*/
trait StrictOptimizedSortedSetOps[A, +CC[X] <: SortedSet[X], +C <: SortedSetOps[A, CC, C]]
extends SortedSetOps[A, CC, C]
with StrictOptimizedIterableOps[A, Set, C] {

override def map[B](f: A => B)(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = {
val b = sortedIterableFactory.newBuilder[B]
val it = iterator
while (it.hasNext) {
b += f(it.next())
}
b.result()
}

override def flatMap[B](f: A => IterableOnce[B])(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = {
val b = sortedIterableFactory.newBuilder[B]
val it = iterator
while (it.hasNext) {
b ++= f(it.next())
}
b.result()
}

override def zip[B](that: Iterable[B])(implicit @implicitNotFound(SortedSetOps.zipOrdMsg) ev: Ordering[(A @uncheckedVariance, B)]): CC[(A @uncheckedVariance, B)] = { // sound bcs of VarianceNot
val b = sortedIterableFactory.newBuilder[(A, B)]
val it1 = iterator
val it2 = that.iterator
while (it1.hasNext && it2.hasNext) {
b += ((it1.next(), it2.next()))
}
b.result()
}

override def collect[B](pf: PartialFunction[A, B])(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = {
val b = sortedIterableFactory.newBuilder[B]
val it = iterator
while (it.hasNext) {
val elem = it.next()
if (pf.isDefinedAt(elem)) {
b += pf.apply(elem)
}
}
b.result()
}
with StrictOptimizedSetOps[A, Set, C] {

override def map[B](f: A => B)(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] =
StrictOptimizedOps.map(iterator, sortedIterableFactory.newBuilder, f)

override def flatMap[B](f: A => IterableOnce[B])(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] =
StrictOptimizedOps.flatMap(iterator, sortedIterableFactory.newBuilder, f)

override def zip[B](that: Iterable[B])(implicit @implicitNotFound(SortedSetOps.zipOrdMsg) ev: Ordering[(A @uncheckedVariance, B)]): CC[(A @uncheckedVariance, B)] =
StrictOptimizedOps.zip(iterator, that.iterator, sortedIterableFactory.newBuilder[(A, B)])

override def collect[B](pf: PartialFunction[A, B])(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] =
StrictOptimizedOps.collect(iterator, sortedIterableFactory.newBuilder, pf)

}
Loading

0 comments on commit 2e37a99

Please sign in to comment.