summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRex Kerr <ichoran@gmail.com>2016-01-14 15:14:53 -0800
committerRex Kerr <ichoran@gmail.com>2016-01-30 18:32:27 -0800
commit6eaae1b969b68ed3dc65a40613a8168b09246256 (patch)
tree35118267c53111a2f9ce7e2a0ddde0ff903fa2eb
parent78f0437ac06e71a94a38721751ef5d36ca62c062 (diff)
downloadscala-6eaae1b969b68ed3dc65a40613a8168b09246256.tar.gz
scala-6eaae1b969b68ed3dc65a40613a8168b09246256.tar.bz2
scala-6eaae1b969b68ed3dc65a40613a8168b09246256.zip
Clarified and expanded which Builders were reusable
This additionally fixes both SI-8648 and SI-9564. Added documentation to Builder to clarify that in general Builders are NOT reusable. Altered implementation of GrowingBuilder to use Growable instance's clear (not valid for a reusable builder, but this one isn't reusable). Added a new marker trait ReusableBuilder that specifies that these builders should be reusable. This trait overrides the clear and result methods while leaving them abstract in order to supply appropriate scaladoc. Made all Array builders Reusable in all cases (by setting capacity to 0 if the original array is returned). (Fixed a poor implmentation of Array[Unit] builder along the way.) Documented which other builders were already reusable (maps, sets, Vector, LazyBuilder, StringBuilder, ListBuffer, etc.).
-rw-r--r--src/library/scala/collection/immutable/ListSet.scala6
-rw-r--r--src/library/scala/collection/immutable/Vector.scala6
-rw-r--r--src/library/scala/collection/mutable/AnyRefMap.scala6
-rw-r--r--src/library/scala/collection/mutable/ArrayBuilder.scala153
-rw-r--r--src/library/scala/collection/mutable/Builder.scala16
-rw-r--r--src/library/scala/collection/mutable/GrowingBuilder.scala4
-rw-r--r--src/library/scala/collection/mutable/LazyBuilder.scala4
-rw-r--r--src/library/scala/collection/mutable/ListBuffer.scala6
-rw-r--r--src/library/scala/collection/mutable/LongMap.scala6
-rw-r--r--src/library/scala/collection/mutable/MapBuilder.scala2
-rw-r--r--src/library/scala/collection/mutable/ReusableBuilder.scala51
-rw-r--r--src/library/scala/collection/mutable/SetBuilder.scala4
-rw-r--r--src/library/scala/collection/mutable/StringBuilder.scala8
-rw-r--r--src/library/scala/collection/mutable/WrappedArrayBuilder.scala13
-rw-r--r--test/junit/scala/collection/ReusableBuildersTest.scala48
15 files changed, 225 insertions, 108 deletions
diff --git a/src/library/scala/collection/immutable/ListSet.scala b/src/library/scala/collection/immutable/ListSet.scala
index a65e25ed6e..98b91f7c84 100644
--- a/src/library/scala/collection/immutable/ListSet.scala
+++ b/src/library/scala/collection/immutable/ListSet.scala
@@ -12,7 +12,7 @@ package immutable
import generic._
import scala.annotation.tailrec
-import mutable.Builder
+import mutable.{Builder, ReusableBuilder}
/** $factoryInfo
* @define Coll immutable.ListSet
@@ -32,8 +32,10 @@ object ListSet extends ImmutableSetFactory[ListSet] {
* a time to a list backed set puts the "squared" in N^2. There is a
* temporary space cost, but it's improbable a list backed set could
* become large enough for this to matter given its pricy element lookup.
+ *
+ * This builder is reusable.
*/
- class ListSetBuilder[Elem](initial: ListSet[Elem]) extends Builder[Elem, ListSet[Elem]] {
+ class ListSetBuilder[Elem](initial: ListSet[Elem]) extends ReusableBuilder[Elem, ListSet[Elem]] {
def this() = this(empty[Elem])
protected val elems = (new mutable.ListBuffer[Elem] ++= initial).reverse
protected val seen = new mutable.HashSet[Elem] ++= initial
diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala
index cd2d3f843b..754196e6c1 100644
--- a/src/library/scala/collection/immutable/Vector.scala
+++ b/src/library/scala/collection/immutable/Vector.scala
@@ -13,7 +13,7 @@ package immutable
import scala.annotation.unchecked.uncheckedVariance
import scala.compat.Platform
import scala.collection.generic._
-import scala.collection.mutable.Builder
+import scala.collection.mutable.{Builder, ReusableBuilder}
import scala.collection.parallel.immutable.ParVector
/** Companion object to the Vector class
@@ -704,8 +704,8 @@ extends AbstractIterator[A]
}
}
-
-final class VectorBuilder[A]() extends Builder[A,Vector[A]] with VectorPointer[A @uncheckedVariance] {
+/** A class to build instances of `Vector`. This builder is reusable. */
+final class VectorBuilder[A]() extends ReusableBuilder[A,Vector[A]] with VectorPointer[A @uncheckedVariance] {
// possible alternative: start with display0 = null, blockIndex = -32, lo = 32
// to avoid allocating initial array if the result will be empty anyways
diff --git a/src/library/scala/collection/mutable/AnyRefMap.scala b/src/library/scala/collection/mutable/AnyRefMap.scala
index af7600ad3d..2ed5bbea60 100644
--- a/src/library/scala/collection/mutable/AnyRefMap.scala
+++ b/src/library/scala/collection/mutable/AnyRefMap.scala
@@ -427,7 +427,11 @@ object AnyRefMap {
def apply(): AnyRefMapBuilder[J, U] = new AnyRefMapBuilder[J, U]
}
- final class AnyRefMapBuilder[K <: AnyRef, V] extends Builder[(K, V), AnyRefMap[K, V]] {
+ /** A builder for instances of `AnyRefMap`.
+ *
+ * This builder can be reused to create multiple instances.
+ */
+ final class AnyRefMapBuilder[K <: AnyRef, V] extends ReusableBuilder[(K, V), AnyRefMap[K, V]] {
private[collection] var elems: AnyRefMap[K, V] = new AnyRefMap[K, V]
def +=(entry: (K, V)): this.type = {
elems += entry
diff --git a/src/library/scala/collection/mutable/ArrayBuilder.scala b/src/library/scala/collection/mutable/ArrayBuilder.scala
index ac78ab823b..549ffef565 100644
--- a/src/library/scala/collection/mutable/ArrayBuilder.scala
+++ b/src/library/scala/collection/mutable/ArrayBuilder.scala
@@ -18,7 +18,7 @@ import scala.reflect.ClassTag
*
* @tparam T the type of the elements for the builder.
*/
-abstract class ArrayBuilder[T] extends Builder[T, Array[T]] with Serializable
+abstract class ArrayBuilder[T] extends ReusableBuilder[T, Array[T]] with Serializable
/** A companion object for array builders.
*
@@ -49,6 +49,8 @@ object ArrayBuilder {
/** A class for array builders for arrays of reference types.
*
+ * This builder can be reused.
+ *
* @tparam T type of elements for the array builder, subtype of `AnyRef` with a `ClassTag` context bound.
*/
@deprecatedInheritance("ArrayBuilder.ofRef is an internal implementation not intended for subclassing.", "2.11.0")
@@ -98,12 +100,13 @@ object ArrayBuilder {
super.++=(xs)
}
- def clear() {
- size = 0
- }
+ def clear() { size = 0 }
def result() = {
- if (capacity != 0 && capacity == size) elems
+ if (capacity != 0 && capacity == size) {
+ capacity = 0
+ elems
+ }
else mkArray(size)
}
@@ -115,7 +118,7 @@ object ArrayBuilder {
override def toString = "ArrayBuilder.ofRef"
}
- /** A class for array builders for arrays of `byte`s. */
+ /** A class for array builders for arrays of `byte`s. It can be reused. */
@deprecatedInheritance("ArrayBuilder.ofByte is an internal implementation not intended for subclassing.", "2.11.0")
class ofByte extends ArrayBuilder[Byte] {
@@ -163,12 +166,13 @@ object ArrayBuilder {
super.++=(xs)
}
- def clear() {
- size = 0
- }
+ def clear() { size = 0 }
def result() = {
- if (capacity != 0 && capacity == size) elems
+ if (capacity != 0 && capacity == size) {
+ capacity = 0
+ elems
+ }
else mkArray(size)
}
@@ -180,7 +184,7 @@ object ArrayBuilder {
override def toString = "ArrayBuilder.ofByte"
}
- /** A class for array builders for arrays of `short`s. */
+ /** A class for array builders for arrays of `short`s. It can be reused. */
@deprecatedInheritance("ArrayBuilder.ofShort is an internal implementation not intended for subclassing.", "2.11.0")
class ofShort extends ArrayBuilder[Short] {
@@ -228,12 +232,13 @@ object ArrayBuilder {
super.++=(xs)
}
- def clear() {
- size = 0
- }
+ def clear() { size = 0 }
def result() = {
- if (capacity != 0 && capacity == size) elems
+ if (capacity != 0 && capacity == size) {
+ capacity = 0
+ elems
+ }
else mkArray(size)
}
@@ -245,7 +250,7 @@ object ArrayBuilder {
override def toString = "ArrayBuilder.ofShort"
}
- /** A class for array builders for arrays of `char`s. */
+ /** A class for array builders for arrays of `char`s. It can be reused. */
@deprecatedInheritance("ArrayBuilder.ofChar is an internal implementation not intended for subclassing.", "2.11.0")
class ofChar extends ArrayBuilder[Char] {
@@ -293,12 +298,13 @@ object ArrayBuilder {
super.++=(xs)
}
- def clear() {
- size = 0
- }
+ def clear() { size = 0 }
def result() = {
- if (capacity != 0 && capacity == size) elems
+ if (capacity != 0 && capacity == size) {
+ capacity = 0
+ elems
+ }
else mkArray(size)
}
@@ -310,7 +316,7 @@ object ArrayBuilder {
override def toString = "ArrayBuilder.ofChar"
}
- /** A class for array builders for arrays of `int`s. */
+ /** A class for array builders for arrays of `int`s. It can be reused. */
@deprecatedInheritance("ArrayBuilder.ofInt is an internal implementation not intended for subclassing.", "2.11.0")
class ofInt extends ArrayBuilder[Int] {
@@ -358,12 +364,13 @@ object ArrayBuilder {
super.++=(xs)
}
- def clear() {
- size = 0
- }
+ def clear() { size = 0 }
def result() = {
- if (capacity != 0 && capacity == size) elems
+ if (capacity != 0 && capacity == size) {
+ capacity = 0
+ elems
+ }
else mkArray(size)
}
@@ -375,7 +382,7 @@ object ArrayBuilder {
override def toString = "ArrayBuilder.ofInt"
}
- /** A class for array builders for arrays of `long`s. */
+ /** A class for array builders for arrays of `long`s. It can be reused. */
@deprecatedInheritance("ArrayBuilder.ofLong is an internal implementation not intended for subclassing.", "2.11.0")
class ofLong extends ArrayBuilder[Long] {
@@ -423,12 +430,13 @@ object ArrayBuilder {
super.++=(xs)
}
- def clear() {
- size = 0
- }
+ def clear() { size = 0 }
def result() = {
- if (capacity != 0 && capacity == size) elems
+ if (capacity != 0 && capacity == size) {
+ capacity = 0
+ elems
+ }
else mkArray(size)
}
@@ -440,7 +448,7 @@ object ArrayBuilder {
override def toString = "ArrayBuilder.ofLong"
}
- /** A class for array builders for arrays of `float`s. */
+ /** A class for array builders for arrays of `float`s. It can be reused. */
@deprecatedInheritance("ArrayBuilder.ofFloat is an internal implementation not intended for subclassing.", "2.11.0")
class ofFloat extends ArrayBuilder[Float] {
@@ -488,12 +496,13 @@ object ArrayBuilder {
super.++=(xs)
}
- def clear() {
- size = 0
- }
+ def clear() { size = 0 }
def result() = {
- if (capacity != 0 && capacity == size) elems
+ if (capacity != 0 && capacity == size) {
+ capacity = 0
+ elems
+ }
else mkArray(size)
}
@@ -505,7 +514,7 @@ object ArrayBuilder {
override def toString = "ArrayBuilder.ofFloat"
}
- /** A class for array builders for arrays of `double`s. */
+ /** A class for array builders for arrays of `double`s. It can be reused. */
@deprecatedInheritance("ArrayBuilder.ofDouble is an internal implementation not intended for subclassing.", "2.11.0")
class ofDouble extends ArrayBuilder[Double] {
@@ -553,12 +562,13 @@ object ArrayBuilder {
super.++=(xs)
}
- def clear() {
- size = 0
- }
+ def clear() { size = 0 }
def result() = {
- if (capacity != 0 && capacity == size) elems
+ if (capacity != 0 && capacity == size) {
+ capacity = 0
+ elems
+ }
else mkArray(size)
}
@@ -570,7 +580,7 @@ object ArrayBuilder {
override def toString = "ArrayBuilder.ofDouble"
}
- /** A class for array builders for arrays of `boolean`s. */
+ /** A class for array builders for arrays of `boolean`s. It can be reused. */
class ofBoolean extends ArrayBuilder[Boolean] {
private var elems: Array[Boolean] = _
@@ -617,12 +627,13 @@ object ArrayBuilder {
super.++=(xs)
}
- def clear() {
- size = 0
- }
+ def clear() { size = 0 }
def result() = {
- if (capacity != 0 && capacity == size) elems
+ if (capacity != 0 && capacity == size) {
+ capacity = 0
+ elems
+ }
else mkArray(size)
}
@@ -634,65 +645,33 @@ object ArrayBuilder {
override def toString = "ArrayBuilder.ofBoolean"
}
- /** A class for array builders for arrays of `Unit` type. */
+ /** A class for array builders for arrays of `Unit` type. It can be reused. */
@deprecatedInheritance("ArrayBuilder.ofUnit is an internal implementation not intended for subclassing.", "2.11.0")
class ofUnit extends ArrayBuilder[Unit] {
- private var elems: Array[Unit] = _
- private var capacity: Int = 0
private var size: Int = 0
- private def mkArray(size: Int): Array[Unit] = {
- val newelems = new Array[Unit](size)
- if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size)
- newelems
- }
-
- private def resize(size: Int) {
- elems = mkArray(size)
- capacity = size
- }
-
- override def sizeHint(size: Int) {
- if (capacity < size) resize(size)
- }
-
- private def ensureSize(size: Int) {
- if (capacity < size || capacity == 0) {
- var newsize = if (capacity == 0) 16 else capacity * 2
- while (newsize < size) newsize *= 2
- resize(newsize)
- }
- }
-
def +=(elem: Unit): this.type = {
- ensureSize(size + 1)
- elems(size) = elem
size += 1
this
}
- override def ++=(xs: TraversableOnce[Unit]): this.type = xs match {
- case xs: WrappedArray.ofUnit =>
- ensureSize(this.size + xs.length)
- Array.copy(xs.array, 0, elems, this.size, xs.length)
- size += xs.length
- this
- case _ =>
- super.++=(xs)
+ override def ++=(xs: TraversableOnce[Unit]): this.type = {
+ size += xs.size
+ this
}
- def clear() {
- size = 0
- }
+ def clear() { size = 0 }
def result() = {
- if (capacity != 0 && capacity == size) elems
- else mkArray(size)
+ val ans = new Array[Unit](size)
+ var i = 0
+ while (i < size) { ans(i) = (); i += 1 }
+ ans
}
override def equals(other: Any): Boolean = other match {
- case x: ofUnit => (size == x.size) && (elems == x.elems)
+ case x: ofUnit => (size == x.size)
case _ => false
}
diff --git a/src/library/scala/collection/mutable/Builder.scala b/src/library/scala/collection/mutable/Builder.scala
index 75560580cc..8d6a0ec69d 100644
--- a/src/library/scala/collection/mutable/Builder.scala
+++ b/src/library/scala/collection/mutable/Builder.scala
@@ -18,6 +18,14 @@ import generic._
* elements to the builder with `+=` and then converting to the required
* collection type with `result`.
*
+ * One cannot assume that a single `Builder` can build more than one
+ * instance of the desired collection. Particular subclasses may allow
+ * such behavior. Otherwise, `result` should be treated as a terminal
+ * operation: after it is called, no further methods should be called on
+ * the builder. Extend the [[collection.mutable.ReusableBuilder]] trait
+ * instead of `Builder` for builders that may be reused to build multiple
+ * instances.
+ *
* @tparam Elem the type of elements that get added to the builder.
* @tparam To the type of collection that it produced.
*
@@ -36,8 +44,10 @@ trait Builder[-Elem, +To] extends Growable[Elem] {
*/
def clear()
- /** Produces a collection from the added elements.
- * The builder's contents are undefined after this operation.
+ /** Produces a collection from the added elements. This is a terminal operation:
+ * the builder's contents are undefined after this operation, and no further
+ * methods should be called.
+ *
* @return a collection containing the elements added to this builder.
*/
def result(): To
@@ -112,6 +122,8 @@ trait Builder[-Elem, +To] extends Growable[Elem] {
* @tparam NewTo the type of collection returned by `f`.
* @return a new builder which is the same as the current builder except
* that a transformation function is applied to this builder's result.
+ *
+ * @note The original builder should no longer be used after `mapResult` is called.
*/
def mapResult[NewTo](f: To => NewTo): Builder[Elem, NewTo] =
new Builder[Elem, NewTo] with Proxy {
diff --git a/src/library/scala/collection/mutable/GrowingBuilder.scala b/src/library/scala/collection/mutable/GrowingBuilder.scala
index c4b5e546aa..27d554d98e 100644
--- a/src/library/scala/collection/mutable/GrowingBuilder.scala
+++ b/src/library/scala/collection/mutable/GrowingBuilder.scala
@@ -15,6 +15,8 @@ import generic._
/** The canonical builder for collections that are growable, i.e. that support an
* efficient `+=` method which adds an element to the collection.
*
+ * GrowableBuilders can produce only a single instance of the collection they are growing.
+ *
* @author Paul Phillips
* @version 2.8
* @since 2.8
@@ -25,6 +27,6 @@ import generic._
class GrowingBuilder[Elem, To <: Growable[Elem]](empty: To) extends Builder[Elem, To] {
protected var elems: To = empty
def +=(x: Elem): this.type = { elems += x; this }
- def clear() { elems = empty }
+ def clear() { empty.clear }
def result: To = elems
}
diff --git a/src/library/scala/collection/mutable/LazyBuilder.scala b/src/library/scala/collection/mutable/LazyBuilder.scala
index ebee38b77f..f0a5e6971a 100644
--- a/src/library/scala/collection/mutable/LazyBuilder.scala
+++ b/src/library/scala/collection/mutable/LazyBuilder.scala
@@ -13,12 +13,14 @@ package mutable
/** A builder that constructs its result lazily. Iterators or iterables to
* be added to this builder with `++=` are not evaluated until `result` is called.
*
+ * This builder can be reused.
+ *
* @since 2.8
*
* @tparam Elem type of the elements for this builder.
* @tparam To type of the collection this builder builds.
*/
-abstract class LazyBuilder[Elem, +To] extends Builder[Elem, To] {
+abstract class LazyBuilder[Elem, +To] extends ReusableBuilder[Elem, To] {
/** The different segments of elements to be added to the builder, represented as iterators */
protected var parts = new ListBuffer[TraversableOnce[Elem]]
def +=(x: Elem): this.type = { parts += List(x); this }
diff --git a/src/library/scala/collection/mutable/ListBuffer.scala b/src/library/scala/collection/mutable/ListBuffer.scala
index 0a483ceb86..02fcced3ac 100644
--- a/src/library/scala/collection/mutable/ListBuffer.scala
+++ b/src/library/scala/collection/mutable/ListBuffer.scala
@@ -46,7 +46,7 @@ final class ListBuffer[A]
with Buffer[A]
with GenericTraversableTemplate[A, ListBuffer]
with BufferLike[A, ListBuffer[A]]
- with Builder[A, List[A]]
+ with ReusableBuilder[A, List[A]]
with SeqForwarder[A]
with Serializable
{
@@ -297,6 +297,10 @@ final class ListBuffer[A]
// Implementation of abstract method in Builder
+ /** Returns the accumulated `List`.
+ *
+ * This method may be called multiple times to obtain snapshots of the list in different stages of construction.
+ */
def result: List[A] = toList
/** Converts this buffer to a list. Takes constant time. The buffer is
diff --git a/src/library/scala/collection/mutable/LongMap.scala b/src/library/scala/collection/mutable/LongMap.scala
index f39a6ba634..ecbb1952af 100644
--- a/src/library/scala/collection/mutable/LongMap.scala
+++ b/src/library/scala/collection/mutable/LongMap.scala
@@ -519,7 +519,11 @@ object LongMap {
def apply(): LongMapBuilder[U] = new LongMapBuilder[U]
}
- final class LongMapBuilder[V] extends Builder[(Long, V), LongMap[V]] {
+ /** A builder for instances of `LongMap`.
+ *
+ * This builder can be reused to create multiple instances.
+ */
+ final class LongMapBuilder[V] extends ReusableBuilder[(Long, V), LongMap[V]] {
private[collection] var elems: LongMap[V] = new LongMap[V]
def +=(entry: (Long, V)): this.type = {
elems += entry
diff --git a/src/library/scala/collection/mutable/MapBuilder.scala b/src/library/scala/collection/mutable/MapBuilder.scala
index a5a6b12ea9..cfc3079f41 100644
--- a/src/library/scala/collection/mutable/MapBuilder.scala
+++ b/src/library/scala/collection/mutable/MapBuilder.scala
@@ -23,7 +23,7 @@ package mutable
* @since 2.8
*/
class MapBuilder[A, B, Coll <: scala.collection.GenMap[A, B] with scala.collection.GenMapLike[A, B, Coll]](empty: Coll)
-extends Builder[(A, B), Coll] {
+extends ReusableBuilder[(A, B), Coll] {
protected var elems: Coll = empty
def +=(x: (A, B)): this.type = {
elems = (elems + x).asInstanceOf[Coll]
diff --git a/src/library/scala/collection/mutable/ReusableBuilder.scala b/src/library/scala/collection/mutable/ReusableBuilder.scala
new file mode 100644
index 0000000000..caab3071b6
--- /dev/null
+++ b/src/library/scala/collection/mutable/ReusableBuilder.scala
@@ -0,0 +1,51 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2016, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+
+package scala
+package collection
+package mutable
+
+import generic._
+
+/** `ReusableBuilder` is a marker trait that indicates that a `Builder`
+ * can be reused to build more than one instance of a collection. In
+ * particular, calling `result` followed by `clear` will produce a
+ * collection and reset the builder to begin building a new collection
+ * of the same type.
+ *
+ * It is up to subclasses to implement this behavior, and to document any
+ * other behavior that varies from standard `ReusableBuilder` usage
+ * (e.g. operations being well-defined after a call to `result`, or allowing
+ * multiple calls to result to obtain different snapshots of a collection under
+ * construction).
+ *
+ * @tparam Elem the type of elements that get added to the builder.
+ * @tparam To the type of collection that it produced.
+ *
+ * @since 2.12
+ */
+trait ReusableBuilder[-Elem, +To] extends Builder[Elem, To] {
+ /** Clears the contents of this builder.
+ * After execution of this method, the builder will contain no elements.
+ *
+ * If executed immediately after a call to `result`, this allows a new
+ * instance of the same type of collection to be built.
+ */
+ override def clear(): Unit // Note: overriding for scaladoc only!
+
+ /** Produces a collection from the added elements.
+ *
+ * After a call to `result`, the behavior of all other methods is undefined
+ * save for `clear`. If `clear` is called, then the builder is reset and
+ * may be used to build another instance.
+ *
+ * @return a collection containing the elements added to this builder.
+ */
+ override def result(): To // Note: overriding for scaladoc only!
+}
diff --git a/src/library/scala/collection/mutable/SetBuilder.scala b/src/library/scala/collection/mutable/SetBuilder.scala
index 01bfdc96ed..5d1e9ffc3a 100644
--- a/src/library/scala/collection/mutable/SetBuilder.scala
+++ b/src/library/scala/collection/mutable/SetBuilder.scala
@@ -17,7 +17,9 @@ package mutable
* @param empty The empty element of the collection.
* @since 2.8
*/
-class SetBuilder[A, Coll <: scala.collection.Set[A] with scala.collection.SetLike[A, Coll]](empty: Coll) extends Builder[A, Coll] {
+class SetBuilder[A, Coll <: scala.collection.Set[A]
+with scala.collection.SetLike[A, Coll]](empty: Coll)
+extends ReusableBuilder[A, Coll] {
protected var elems: Coll = empty
def +=(x: A): this.type = { elems = elems + x; this }
def clear() { elems = empty }
diff --git a/src/library/scala/collection/mutable/StringBuilder.scala b/src/library/scala/collection/mutable/StringBuilder.scala
index c56d40786e..b5b9498374 100644
--- a/src/library/scala/collection/mutable/StringBuilder.scala
+++ b/src/library/scala/collection/mutable/StringBuilder.scala
@@ -33,7 +33,7 @@ final class StringBuilder(private val underlying: JavaStringBuilder)
with java.lang.CharSequence
with IndexedSeq[Char]
with StringLike[StringBuilder]
- with Builder[Char, String]
+ with ReusableBuilder[Char, String]
with Serializable {
override protected[this] def thisCollection: StringBuilder = this
@@ -435,7 +435,11 @@ final class StringBuilder(private val underlying: JavaStringBuilder)
*/
override def mkString = toString
- /** Returns the result of this Builder (a String)
+ /** Returns the result of this Builder (a String).
+ *
+ * If this method is called multiple times, each call will result in a snapshot of the buffer at that point in time.
+ * In particular, a `StringBuilder` can be used to build multiple independent strings by emptying the buffer with `clear`
+ * after each call to `result`.
*
* @return the string assembled by this StringBuilder
*/
diff --git a/src/library/scala/collection/mutable/WrappedArrayBuilder.scala b/src/library/scala/collection/mutable/WrappedArrayBuilder.scala
index bfe95a11ab..c4781321d7 100644
--- a/src/library/scala/collection/mutable/WrappedArrayBuilder.scala
+++ b/src/library/scala/collection/mutable/WrappedArrayBuilder.scala
@@ -17,12 +17,14 @@ import scala.runtime.ScalaRunTime._
/** A builder class for arrays.
*
+ * This builder can be reused.
+ *
* @tparam A type of elements that can be added to this builder.
* @param tag class tag for objects of type `A`.
*
* @since 2.8
*/
-class WrappedArrayBuilder[A](tag: ClassTag[A]) extends Builder[A, WrappedArray[A]] {
+class WrappedArrayBuilder[A](tag: ClassTag[A]) extends ReusableBuilder[A, WrappedArray[A]] {
@deprecated("use tag instead", "2.10.0")
val manifest: ClassTag[A] = tag
@@ -73,12 +75,13 @@ class WrappedArrayBuilder[A](tag: ClassTag[A]) extends Builder[A, WrappedArray[A
this
}
- def clear() {
- size = 0
- }
+ def clear() { size = 0 }
def result() = {
- if (capacity != 0 && capacity == size) elems
+ if (capacity != 0 && capacity == size) {
+ capacity = 0
+ elems
+ }
else mkArray(size)
}
diff --git a/test/junit/scala/collection/ReusableBuildersTest.scala b/test/junit/scala/collection/ReusableBuildersTest.scala
new file mode 100644
index 0000000000..8dd1a37adf
--- /dev/null
+++ b/test/junit/scala/collection/ReusableBuildersTest.scala
@@ -0,0 +1,48 @@
+package scala.collection
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.Test
+
+/* Tests various maps by making sure they all agree on the same answers. */
+@RunWith(classOf[JUnit4])
+class ReusableBuildersTest {
+ // GrowingBuilders are NOT reusable but can clear themselves
+ @Test
+ def test_SI8648() {
+ val b = collection.mutable.HashSet.newBuilder[Int]
+ b += 3
+ b.clear
+ assert(!b.isInstanceOf[collection.mutable.ReusableBuilder[_,_]])
+ assert(b.isInstanceOf[collection.mutable.GrowingBuilder[_,_]])
+ assert(b.result == Set[Int]())
+ }
+
+ // ArrayBuilders ARE reusable, regardless of whether they returned their internal array or not
+ @Test
+ def test_SI9564() {
+ val b = Array.newBuilder[Float]
+ b += 3f
+ val three = b.result
+ b.clear
+ b ++= (1 to 16).map(_.toFloat)
+ val sixteen = b.result
+ b.clear
+ b += 0f
+ val zero = b.result
+ assert(b.isInstanceOf[collection.mutable.ReusableBuilder[_,_]])
+ assert(three.toList == 3 :: Nil)
+ assert(sixteen.toList == (1 to 16))
+ assert(zero.toList == 0 :: Nil)
+ }
+
+ @Test
+ def test_reusability() {
+ val bl = List.newBuilder[String]
+ val bv = Vector.newBuilder[String]
+ val ba = collection.mutable.ArrayBuffer.newBuilder[String]
+ assert(bl.isInstanceOf[collection.mutable.ReusableBuilder[_, _]])
+ assert(bv.isInstanceOf[collection.mutable.ReusableBuilder[_, _]])
+ assert(!ba.isInstanceOf[collection.mutable.ReusableBuilder[_, _]])
+ }
+}