diff --git a/compat/src/main/scala-2.11_2.12/scala/collection/compat/CompatImpl.scala b/compat/src/main/scala-2.11_2.12/scala/collection/compat/CompatImpl.scala index f6f43961..569cda09 100644 --- a/compat/src/main/scala-2.11_2.12/scala/collection/compat/CompatImpl.scala +++ b/compat/src/main/scala-2.11_2.12/scala/collection/compat/CompatImpl.scala @@ -12,14 +12,58 @@ package scala.collection.compat +import scala.reflect.ClassTag import scala.collection.generic.CanBuildFrom -import scala.collection.mutable.Builder import scala.collection.{immutable => i, mutable => m} +/* builder optimized for a single ++= call, which returns identity on result if possible + * and defers to the underlying builder if not. + */ +private final class IdentityPreservingBuilder[A, CC[X] <: TraversableOnce[X]](that: m.Builder[A, CC[A]])(implicit ct: ClassTag[CC[A]]) + extends m.Builder[A, CC[A]] { + + //invariant: ruined => (collection == null) + var collection: CC[A] = null.asInstanceOf[CC[A]] + var ruined = false + + private[this] def ruin(): Unit = { + if(collection != null) that ++= collection + collection = null.asInstanceOf[CC[A]] + ruined = true + } + + override def ++=(elems: TraversableOnce[A]): this.type = + elems match { + case ct(ca) if collection == null && !ruined => { + collection = ca + this + } + case _ => { + ruin() + that ++= elems + this + } + } + + def +=(elem: A): this.type = { + ruin() + that += elem + this + } + + def clear(): Unit = { + collection = null.asInstanceOf[CC[A]] + if (ruined) that.clear() + ruined = false + } + + def result(): CC[A] = if(collection == null) that.result() else collection +} + private[compat] object CompatImpl { - def simpleCBF[A, C](f: => Builder[A, C]): CanBuildFrom[Any, A, C] = new CanBuildFrom[Any, A, C] { - def apply(from: Any): Builder[A, C] = apply() - def apply(): Builder[A, C] = f + def simpleCBF[A, C](f: => m.Builder[A, C]): CanBuildFrom[Any, A, C] = new CanBuildFrom[Any, A, C] { + def apply(from: Any): m.Builder[A, C] = apply() + def apply(): m.Builder[A, C] = f } type ImmutableBitSetCC[X] = ({ type L[_] = i.BitSet })#L[X] diff --git a/compat/src/main/scala-2.11_2.12/scala/collection/compat/PackageShared.scala b/compat/src/main/scala-2.11_2.12/scala/collection/compat/PackageShared.scala index 14af479d..0a8fbb0b 100644 --- a/compat/src/main/scala-2.11_2.12/scala/collection/compat/PackageShared.scala +++ b/compat/src/main/scala-2.11_2.12/scala/collection/compat/PackageShared.scala @@ -45,8 +45,15 @@ private[compat] trait PackageShared { } implicit def genericCompanionToCBF[A, CC[X] <: GenTraversable[X]]( - fact: GenericCompanion[CC]): CanBuildFrom[Any, A, CC[A]] = - simpleCBF(fact.newBuilder[A]) + fact: GenericCompanion[CC]): CanBuildFrom[Any, A, CC[A]] = { + val builder: m.Builder[A, CC[A]] = fact match { + case c.Seq | i.Seq => new IdentityPreservingBuilder[A, i.Seq](i.Seq.newBuilder[A]) + case c.LinearSeq | i.LinearSeq => + new IdentityPreservingBuilder[A, i.LinearSeq](i.LinearSeq.newBuilder[A]) + case _ => fact.newBuilder[A] + } + simpleCBF(builder) + } implicit def sortedSetCompanionToCBF[A: Ordering, CC[X] <: c.SortedSet[X] with c.SortedSetLike[X, CC[X]]]( diff --git a/compat/src/test/scala/test/scala/collection/CollectionTest.scala b/compat/src/test/scala/test/scala/collection/CollectionTest.scala index f9b7ac89..3401d5ed 100644 --- a/compat/src/test/scala/test/scala/collection/CollectionTest.scala +++ b/compat/src/test/scala/test/scala/collection/CollectionTest.scala @@ -17,6 +17,7 @@ import org.junit.Test import scala.collection.compat._ import scala.collection.immutable.BitSet +import scala.collection.LinearSeq class CollectionTest { @Test @@ -40,6 +41,11 @@ class CollectionTest { //val mT: Map[Int, String] = m assertEquals(Map(1 -> "a", 2 -> "b"), m) assertTrue(m.isInstanceOf[Map[_, _]]) + + // Stream.to(Seq) doesn't evaluate the stream + val strm = 1 #:: {throw new Exception("not lazy")} #:: Stream.empty[Int] + val strmsq: Seq[Int] = strm.to(Seq) + var strmln: LinearSeq[Int] = strm.to(LinearSeq) } @Test