diff --git a/README.md b/README.md index 9c2e4f2c02..32f4d59c11 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,8 @@ See the [CONTRIBUTING](CONTRIBUTING.md) file. - [x] `foldLeft` - [x] `foldRight` - [x] `get` +- [x] `getOrElse` +- [x] `getOrElseUpdate` - [x] `head` - [x] `indexWhere` - [x] `isDefinedAt` @@ -88,7 +90,7 @@ See the [CONTRIBUTING](CONTRIBUTING.md) file. - [x] `drop` - [x] `empty` - [x] `filter` / `filterNot` -- [ ] `groupBy` +- [x] `groupBy` - [x] `intersect` - [x] `partition` - [x] `range` diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/HashSetBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/HashSetBenchmark.scala index a7f95006a8..0018327302 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/HashSetBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/HashSetBenchmark.scala @@ -59,4 +59,10 @@ class HashSetBenchmark { @Benchmark def map(bh: Blackhole): Unit = bh.consume(xs.map(x => x + 1)) + @Benchmark + def groupBy(bh: Blackhole): Unit = { + val result = xs.groupBy(_ % 5) + bh.consume(result) + } + } diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/ImmutableArrayBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/ImmutableArrayBenchmark.scala index e39d7a69aa..ecd3b3ae2b 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/ImmutableArrayBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/ImmutableArrayBenchmark.scala @@ -87,4 +87,10 @@ class ImmutableArrayBenchmark { @Benchmark def map(bh: Blackhole): Unit = bh.consume(xs.map(x => x + 1)) + @Benchmark + def groupBy(bh: Blackhole): Unit = { + val result = xs.groupBy(_ % 5) + bh.consume(result) + } + } diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/LazyListBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/LazyListBenchmark.scala index e5757c6e45..1722015a9a 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/LazyListBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/LazyListBenchmark.scala @@ -79,4 +79,10 @@ class LazyListBenchmark { @Benchmark def map(bh: Blackhole): Unit = bh.consume(xs.map(x => x + 1)) + @Benchmark + def groupBy(bh: Blackhole): Unit = { + val result = xs.groupBy(_ % 5) + bh.consume(result) + } + } diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/ListBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/ListBenchmark.scala index 12692cd872..13f0d56472 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/ListBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/ListBenchmark.scala @@ -87,4 +87,10 @@ class ListBenchmark { @Benchmark def map(bh: Blackhole): Unit = bh.consume(xs.map(x => x + 1)) + @Benchmark + def groupBy(bh: Blackhole): Unit = { + val result = xs.groupBy(_ % 5) + bh.consume(result) + } + } diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/PrimitiveArrayBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/PrimitiveArrayBenchmark.scala index 939bbc4a75..5f04d0281d 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/PrimitiveArrayBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/PrimitiveArrayBenchmark.scala @@ -3,7 +3,10 @@ package strawman.collection.immutable import java.util.concurrent.TimeUnit import org.openjdk.jmh.annotations._ -import scala.{Any, AnyRef, Int, Unit} +import org.openjdk.jmh.infra.Blackhole + +import scala.{Any, AnyRef, Int, Long, Unit} +import scala.Predef.intWrapper @BenchmarkMode(scala.Array(Mode.AverageTime)) @OutputTimeUnit(TimeUnit.NANOSECONDS) @@ -13,46 +16,81 @@ import scala.{Any, AnyRef, Int, Unit} @State(Scope.Benchmark) class PrimitiveArrayBenchmark { - @Param(scala.Array("8", "64", "512", "4096", "32768", "262144"/*, "2097152"*/)) + @Param(scala.Array("0", "1", "2", "3", "4", "7", "8", "15", "16", "17", "39", "282", "73121", "7312102")) var size: Int = _ var xs: ImmutableArray[Int] = _ - var obj: Int = _ + var xss: scala.Array[ImmutableArray[Int]] = _ + var randomIndices: scala.Array[Int] = _ @Setup(Level.Trial) def initData(): Unit = { - xs = ImmutableArray.fill(size)(obj) - obj = 123 + def freshCollection() = ImmutableArray((1 to size): _*) + xs = freshCollection() + xss = scala.Array.fill(1000)(freshCollection()) + if (size > 0) { + randomIndices = scala.Array.fill(1000)(scala.util.Random.nextInt(size)) + } } @Benchmark - def cons(): Any = { + // @OperationsPerInvocation(size) + def cons(bh: Blackhole): Unit = { var ys = ImmutableArray.empty[Int] var i = 0 while (i < size) { - ys = ys :+ obj - i += 1 + ys = ys :+ i + i = i + 1 } - ys + bh.consume(ys) } @Benchmark - def uncons(): Any = xs.tail + def uncons(bh: Blackhole): Unit = bh.consume(xs.tail) + + @Benchmark + def concat(bh: Blackhole): Unit = bh.consume(xs ++ xs) + + @Benchmark + def foreach(bh: Blackhole): Unit = xs.foreach(x => bh.consume(x)) + + @Benchmark + // @OperationsPerInvocation(size) + def foreach_while(bh: Blackhole): Unit = { + var ys = xs + while (ys.nonEmpty) { + bh.consume(ys.head) + ys = ys.tail + } + } @Benchmark - def concat(): Any = xs ++ xs + @OperationsPerInvocation(1000) + def lookupLast(bh: Blackhole): Unit = { + var i = 0 + while (i < 1000) { + bh.consume(xss(i)(size - 1)) + i = i + 1 + } + } @Benchmark - def foreach(): Any = { - var n = 0 - xs.foreach(x => if (x == 0) n += 1) - n + @OperationsPerInvocation(1000) + def randomLookup(bh: Blackhole): Unit = { + var i = 0 + while (i < 1000) { + bh.consume(xs(randomIndices(i))) + i = i + 1 + } } @Benchmark - def lookup(): Any = xs(size - 1) + def map(bh: Blackhole): Unit = bh.consume(xs.map(x => x + 1)) @Benchmark - def map(): Any = xs.map(x => if (x == 0) "foo" else "bar") + def groupBy(bh: Blackhole): Unit = { + val result = xs.groupBy(_ % 5) + bh.consume(result) + } } diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaHashSetBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaHashSetBenchmark.scala index a8d55aa758..a4340d67f8 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaHashSetBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaHashSetBenchmark.scala @@ -57,4 +57,10 @@ class ScalaHashSetBenchmark { @Benchmark def map(bh: Blackhole): Unit = bh.consume(xs.map(x => x + 1)) + @Benchmark + def groupBy(bh: Blackhole): Unit = { + val result = xs.groupBy(_ % 5) + bh.consume(result) + } + } diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaListBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaListBenchmark.scala index ec43e1ba58..accf420719 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaListBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaListBenchmark.scala @@ -87,4 +87,10 @@ class ScalaListBenchmark { @Benchmark def map(bh: Blackhole): Unit = bh.consume(xs.map(x => x + 1)) + @Benchmark + def groupBy(bh: Blackhole): Unit = { + val result = xs.groupBy(_ % 5) + bh.consume(result) + } + } diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaTreeSetBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaTreeSetBenchmark.scala index 792f74402f..aa32ca1a66 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaTreeSetBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaTreeSetBenchmark.scala @@ -59,4 +59,10 @@ class ScalaTreeSetBenchmark { @Benchmark def map(bh: Blackhole): Unit = bh.consume(xs.map(x => x + 1)) + @Benchmark + def groupBy(bh: Blackhole): Unit = { + val result = xs.groupBy(_ % 5) + bh.consume(result) + } + } diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaVectorBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaVectorBenchmark.scala index cad1a8648e..dafe7e93c8 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaVectorBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaVectorBenchmark.scala @@ -3,7 +3,10 @@ package strawman.collection.immutable import java.util.concurrent.TimeUnit import org.openjdk.jmh.annotations._ -import scala.{Any, AnyRef, Int, Unit} +import org.openjdk.jmh.infra.Blackhole + +import scala.{Any, AnyRef, Int, Long, Unit} +import scala.Predef.intWrapper @BenchmarkMode(scala.Array(Mode.AverageTime)) @OutputTimeUnit(TimeUnit.NANOSECONDS) @@ -16,43 +19,67 @@ class ScalaVectorBenchmark { @Param(scala.Array("8", "64", "512", "4096", "32768", "262144"/*, "2097152"*/)) var size: Int = _ - var xs: scala.Vector[AnyRef] = _ - var obj: Any = _ + var xs: scala.Vector[Long] = _ + var xss: scala.Array[scala.Vector[Long]] = _ + var randomIndices: scala.Array[Int] = _ @Setup(Level.Trial) def initData(): Unit = { - xs = scala.Vector.fill(size)("") - obj = "" + def freshCollection() = scala.Vector((1 to size).map(_.toLong): _*) + xs = freshCollection() + xss = scala.Array.fill(1000)(freshCollection()) + if (size > 0) { + randomIndices = scala.Array.fill(1000)(scala.util.Random.nextInt(size)) + } } @Benchmark - def cons(): Any = { - var ys = scala.Vector.empty[Any] - var i = 0 + def cons(bh: Blackhole): Unit = { + var ys = scala.Vector.empty[Long] + var i = 0L while (i < size) { - ys = ys :+ obj + ys = ys :+ i i += 1 } - ys + bh.consume(ys) } @Benchmark - def uncons(): Any = xs.tail + def uncons(bh: Blackhole): Unit = bh.consume(xs.tail) @Benchmark - def concat(): Any = xs ++ xs + def concat(bh: Blackhole): Unit = bh.consume(xs ++ xs) @Benchmark - def foreach(): Any = { - var n = 0 - xs.foreach(x => if (x eq null) n += 1) - n + def foreach(bh: Blackhole): Unit = xs.foreach(x => bh.consume(x)) + + @Benchmark + @OperationsPerInvocation(1000) + def lookupLast(bh: Blackhole): Unit = { + var i = 0 + while (i < 1000) { + bh.consume(xss(i)(size - 1)) + i = i + 1 + } } @Benchmark - def lookup(): Any = xs(size - 1) + @OperationsPerInvocation(1000) + def randomLookup(bh: Blackhole): Unit = { + var i = 0 + while (i < 1000) { + bh.consume(xs(randomIndices(i))) + i = i + 1 + } + } @Benchmark - def map(): Any = xs.map(x => if (x eq null) "foo" else "bar") + def map(bh: Blackhole): Unit = bh.consume(xs.map(x => x + 1)) + + @Benchmark + def groupBy(bh: Blackhole): Unit = { + val result = xs.groupBy(_ % 5) + bh.consume(result) + } } diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/TreeSetBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/TreeSetBenchmark.scala index fb0db16087..c8f521fb93 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/TreeSetBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/TreeSetBenchmark.scala @@ -59,4 +59,10 @@ class TreeSetBenchmark { @Benchmark def map(bh: Blackhole): Unit = bh.consume(xs.map(x => x + 1)) + @Benchmark + def groupBy(bh: Blackhole): Unit = { + val result = xs.groupBy(_ % 5) + bh.consume(result) + } + } diff --git a/benchmarks/time/src/main/scala/strawman/collection/mutable/ArrayBufferBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/mutable/ArrayBufferBenchmark.scala index 8a19f0070b..c1c8b20491 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/mutable/ArrayBufferBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/mutable/ArrayBufferBenchmark.scala @@ -78,4 +78,10 @@ class ArrayBufferBenchmark { @Benchmark def map(bh: Blackhole): Unit = bh.consume(xs.map(x => x + 1)) + @Benchmark + def groupBy(bh: Blackhole): Unit = { + val result = xs.groupBy(_ % 5) + bh.consume(result) + } + } diff --git a/benchmarks/time/src/main/scala/strawman/collection/mutable/ListBufferBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/mutable/ListBufferBenchmark.scala index cec46694f4..00e4765002 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/mutable/ListBufferBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/mutable/ListBufferBenchmark.scala @@ -77,4 +77,10 @@ class ListBufferBenchmark { @Benchmark def map(bh: Blackhole): Unit = bh.consume(xs.map(x => x + 1)) + @Benchmark + def groupBy(bh: Blackhole): Unit = { + val result = xs.groupBy(_ % 5) + bh.consume(result) + } + } diff --git a/src/main/scala/strawman/collection/Iterable.scala b/src/main/scala/strawman/collection/Iterable.scala index 8fbd85e6c1..ace7db9afa 100644 --- a/src/main/scala/strawman/collection/Iterable.scala +++ b/src/main/scala/strawman/collection/Iterable.scala @@ -226,6 +226,21 @@ trait IterableOps[+A, +CC[X], +C] extends Any { def slice(from: Int, until: Int): C = fromSpecificIterable(View.Take(View.Drop(coll, from), until - from)) + /** Partitions this $coll into a map of ${coll}s according to some discriminator function. + * + * Note: When applied to a view or a lazy collection it will always force the elements. + * + * @param f the discriminator function. + * @tparam K the type of keys returned by the discriminator function. + * @return A map from keys to ${coll}s such that the following invariant holds: + * {{{ + * (xs groupBy f)(k) = xs filter (x => f(x) == k) + * }}} + * That is, every key `k` is bound to a $coll of those elements `x` + * for which `f(x)` equals `k`. + * + */ + def groupBy[K](f: A => K): immutable.Map[K, C] /** Map */ def map[B](f: A => B): CC[B] = fromIterable(View.Map(coll, f)) @@ -268,6 +283,9 @@ trait Buildable[+A, +C] extends Any with IterableOps[A, AnyConstr, C] { (l.result(), r.result()) } + def groupBy[K](f: A => K): immutable.Map[K, C] = + generic.GroupBy.strict(f, coll, () => newBuilder) + // one might also override other transforms here to avoid generating // iterators if it helps efficiency. } diff --git a/src/main/scala/strawman/collection/Map.scala b/src/main/scala/strawman/collection/Map.scala index b6d7a21884..6ca20cb6fc 100644 --- a/src/main/scala/strawman/collection/Map.scala +++ b/src/main/scala/strawman/collection/Map.scala @@ -27,6 +27,22 @@ trait MapOps[K, +V, +CC[X, Y] <: Map[X, Y], +C <: Map[K, V]] */ def get(key: K): Option[V] + /** Returns the value associated with a key, or a default value if the key is not contained in the map. + * @param key the key. + * @param default a computation that yields a default value in case no binding for `key` is + * found in the map. + * @tparam V1 the result type of the default computation. + * @return the value associated with `key` if it exists, + * otherwise the result of the `default` computation. + * + * @usecase def getOrElse(key: K, default: => V): V + * @inheritdoc + */ + def getOrElse[V1 >: V](key: K, default: => V1): V1 = get(key) match { + case Some(v) => v + case None => default + } + /** Retrieves the value which is associated with the given key. This * method invokes the `default` method of the map if there is no mapping * from the given key to a value. Unless overridden, the `default` method throws a diff --git a/src/main/scala/strawman/collection/View.scala b/src/main/scala/strawman/collection/View.scala index 24d2508512..e8d0267562 100644 --- a/src/main/scala/strawman/collection/View.scala +++ b/src/main/scala/strawman/collection/View.scala @@ -1,5 +1,8 @@ package strawman.collection +import strawman.collection.immutable.ImmutableArray +import strawman.collection.mutable.{ArrayBuffer, Builder} + import scala.{Any, Boolean, Equals, Int, Nothing, annotation} import scala.Predef.intWrapper @@ -13,6 +16,20 @@ trait View[+A] extends Iterable[A] with IterableOps[A, View, View[A]] { fromIterable(coll) override def className = "View" + + def groupBy[K](f: (A) => K): immutable.Map[K, View[A]] = { + val m = mutable.Map.empty[K, Builder[A, ImmutableArray[A]]] + for (elem <- coll) { + val key = f(elem) + val bldr = m.getOrElseUpdate(key, ImmutableArray.newBuilder()) + bldr += elem + } + var result = immutable.Map.empty[K, View[A]] + m.foreach { case (k, v) => + result = result + ((k, v.result().view)) + } + result + } } /** This object reifies operations on views as case classes */ diff --git a/src/main/scala/strawman/collection/generic/GroupBy.scala b/src/main/scala/strawman/collection/generic/GroupBy.scala new file mode 100644 index 0000000000..98b0c07c31 --- /dev/null +++ b/src/main/scala/strawman/collection/generic/GroupBy.scala @@ -0,0 +1,32 @@ +package strawman +package collection +package generic + +import strawman.collection.mutable.Builder + +object GroupBy { + + /** + * Generic implementation of `groupBy` that relies on a `Builder` + * @param f Function that computes the key of an element + * @param coll Collection on which to apply the `groupBy` + * @param newBuilder Builder for groups + * @tparam A Type of elements + * @tparam K Type of keys + * @tparam C Type of the collection + */ + def strict[A, K, C](f: A => K, coll: Iterable[A], newBuilder: () => Builder[A, C]): immutable.Map[K, C] = { + val m = mutable.Map.empty[K, Builder[A, C]] + for (elem <- coll) { + val key = f(elem) + val bldr = m.getOrElseUpdate(key, newBuilder()) + bldr += elem + } + var result = immutable.Map.empty[K, C] + m.foreach { case (k, v) => + result = result + ((k, v.result())) + } + result + } + +} diff --git a/src/main/scala/strawman/collection/immutable/BitSet.scala b/src/main/scala/strawman/collection/immutable/BitSet.scala index b704e69bc4..dfd51d68eb 100644 --- a/src/main/scala/strawman/collection/immutable/BitSet.scala +++ b/src/main/scala/strawman/collection/immutable/BitSet.scala @@ -3,7 +3,7 @@ package collection package immutable import BitSetOps.{LogWL, updateArray} -import mutable.Builder +import mutable.{Builder, ImmutableBuilder} import scala.{Array, Boolean, Int, Long, Ordering, SerialVersionUID, Serializable, Unit} import scala.Predef.require @@ -22,6 +22,7 @@ sealed abstract class BitSet with collection.BitSet with SortedSetOps[Int, SortedSet, BitSet] with collection.BitSetOps[BitSet] + with Buildable[Int, BitSet] with Serializable { def empty: BitSet = BitSet.empty @@ -30,7 +31,7 @@ sealed abstract class BitSet protected[this] def fromSpecificIterable(coll: collection.Iterable[Int]): BitSet = BitSet.fromSpecificIterable(coll) protected[this] def sortedFromIterable[B : Ordering](it: collection.Iterable[B]): SortedSet[B] = SortedSet.sortedFromIterable(it) - + protected[this] def newBuilder: Builder[Int, BitSet] = BitSet.newBuilder() protected[collection] def fromBitMaskNoCopy(elems: Array[Long]): BitSet = BitSet.fromBitMaskNoCopy(elems) @@ -66,6 +67,11 @@ object BitSet extends SpecificIterableFactory[Int, BitSet] { case _ => empty.++(it) } + def empty: BitSet = new BitSet1(0L) + + def newBuilder(): Builder[Int, BitSet] = + mutable.BitSet.newBuilder().mapResult(bs => fromBitMaskNoCopy(bs.elems)) + private def createSmall(a: Long, b: Long): BitSet = if (b == 0L) new BitSet1(a) else new BitSet2(a, b) /** A bitset containing all the bits in an array */ @@ -91,8 +97,6 @@ object BitSet extends SpecificIterableFactory[Int, BitSet] { else new BitSetN(elems) } - def empty: BitSet = new BitSet1(0L) - @SerialVersionUID(2260107458435649300L) class BitSet1(val elems: Long) extends BitSet { protected[collection] def nwords = 1 diff --git a/src/main/scala/strawman/collection/immutable/HashMap.scala b/src/main/scala/strawman/collection/immutable/HashMap.scala index 26778f57c5..af0ed4cc81 100644 --- a/src/main/scala/strawman/collection/immutable/HashMap.scala +++ b/src/main/scala/strawman/collection/immutable/HashMap.scala @@ -1,13 +1,15 @@ package strawman package collection.immutable -import collection.{Iterator, MapFactory} +import collection.{Buildable, Iterator, MapFactory} import collection.Hashing.{computeHash, keepBits} import scala.annotation.unchecked.{uncheckedVariance => uV} -import scala.{Any, AnyRef, Array, Boolean, `inline`, Int, math, NoSuchElementException, None, Nothing, Option, SerialVersionUID, Serializable, Some, Unit, sys} +import scala.{Any, AnyRef, Array, Boolean, Int, NoSuchElementException, None, Nothing, Option, SerialVersionUID, Serializable, Some, Unit, `inline`, math, sys} import java.lang.{Integer, String, System} +import strawman.collection.mutable.{Builder, ImmutableBuilder} + /** This class implements immutable maps using a hash trie. * * '''Note:''' The builder of this hash map may return specialized representations for small maps. @@ -28,8 +30,9 @@ import java.lang.{Integer, String, System} @SerialVersionUID(2L) sealed trait HashMap[K, +V] extends Map[K, V] - with MapOps[K, V, HashMap, HashMap[K, V]] - with Serializable { + with MapOps[K, V, HashMap, HashMap[K, V]] + with Buildable[(K, V), HashMap[K, V]] + with Serializable { import HashMap.{bufferSize, liftMerger, Merger, MergeFunction, nullToEmpty} @@ -40,6 +43,8 @@ sealed trait HashMap[K, +V] protected[this] def mapFromIterable[K2, V2](it: collection.Iterable[(K2, V2)]): HashMap[K2, V2] = HashMap.fromIterable(it) + protected[this] def newBuilder: Builder[(K, V @uV), HashMap[K, V]] = HashMap.newBuilder() + def remove(key: K): HashMap[K, V] = removed0(key, computeHash(key), 0) def updated[V1 >: V](key: K, value: V1): HashMap[K, V1] = @@ -106,6 +111,11 @@ object HashMap extends MapFactory[HashMap] { case _ => empty ++ it } + def newBuilder[K, V](): Builder[(K, V), HashMap[K, V]] = + new ImmutableBuilder[(K, V), HashMap[K, V]](empty) { + def add(elem: (K, V)): this.type = { elems = elems + elem; this } + } + private[collection] abstract class Merger[A, B] { def apply(kv1: (A, B), kv2: (A, B)): (A, B) def invert: Merger[A, B] diff --git a/src/main/scala/strawman/collection/immutable/HashSet.scala b/src/main/scala/strawman/collection/immutable/HashSet.scala index a207775d5c..d1dfa7927c 100644 --- a/src/main/scala/strawman/collection/immutable/HashSet.scala +++ b/src/main/scala/strawman/collection/immutable/HashSet.scala @@ -2,10 +2,10 @@ package strawman package collection package immutable -import mutable.Builder +import mutable.{Builder, ImmutableBuilder} import Hashing.computeHash -import scala.{Any, AnyRef, Array, Boolean, `inline`, Int, NoSuchElementException, SerialVersionUID, Serializable, Unit, sys} +import scala.{Any, AnyRef, Array, Boolean, Int, NoSuchElementException, SerialVersionUID, Serializable, Unit, `inline`, sys} import scala.Predef.assert import java.lang.Integer @@ -25,8 +25,9 @@ import java.lang.Integer @SerialVersionUID(2L) sealed trait HashSet[A] extends Set[A] - with SetOps[A, HashSet, HashSet[A]] - with Serializable { + with SetOps[A, HashSet, HashSet[A]] + with Buildable[A, HashSet[A]] + with Serializable { import HashSet.nullToEmpty @@ -34,6 +35,8 @@ sealed trait HashSet[A] protected[this] def fromSpecificIterable(coll: collection.Iterable[A]): HashSet[A] = fromIterable(coll) + protected[this] def newBuilder: Builder[A, HashSet[A]] = HashSet.newBuilder() + def contains(elem: A): Boolean = get0(elem, computeHash(elem), 0) def incl(elem: A): HashSet[A] = updated0(elem, computeHash(elem), 0) @@ -62,6 +65,11 @@ object HashSet extends IterableFactory[HashSet] { def empty[A]: HashSet[A] = EmptyHashSet.asInstanceOf[HashSet[A]] + def newBuilder[A](): Builder[A, HashSet[A]] = + new ImmutableBuilder[A, HashSet[A]](empty) { + def add(elem: A): this.type = { elems = elems + elem; this } + } + private object EmptyHashSet extends HashSet[Any] { def iterator(): Iterator[Any] = Iterator.empty diff --git a/src/main/scala/strawman/collection/immutable/ImmutableArray.scala b/src/main/scala/strawman/collection/immutable/ImmutableArray.scala index 27751fd422..4c26b1d8c7 100644 --- a/src/main/scala/strawman/collection/immutable/ImmutableArray.scala +++ b/src/main/scala/strawman/collection/immutable/ImmutableArray.scala @@ -1,7 +1,7 @@ package strawman.collection.immutable -import strawman.collection.mutable.ArrayBuffer -import strawman.collection.{IterableFactory, IterableOnce, Iterator, View} +import strawman.collection.mutable.{ArrayBuffer, Builder, ImmutableBuilder} +import strawman.collection.{Buildable, IterableFactory, IterableOnce, Iterator, View} import scala.{Any, Boolean, Int, Nothing} import scala.runtime.ScalaRunTime @@ -12,12 +12,17 @@ import scala.Predef.{???, intWrapper} * * Supports efficient indexed access and has a small memory footprint. */ -class ImmutableArray[+A] private (private val elements: scala.Array[Any]) extends IndexedSeq[A] with SeqOps[A, ImmutableArray, ImmutableArray[A]] { +class ImmutableArray[+A] private (private val elements: scala.Array[Any]) + extends IndexedSeq[A] + with IndexedSeqOps[A, ImmutableArray, ImmutableArray[A]] + with Buildable[A, ImmutableArray[A]] { def iterableFactory: IterableFactory[ImmutableArray] = ImmutableArray protected[this] def fromSpecificIterable(coll: strawman.collection.Iterable[A]): ImmutableArray[A] = fromIterable(coll) + protected[this] def newBuilder: Builder[A, ImmutableArray[A]] = ImmutableArray.newBuilder() + def length: Int = elements.length override def knownSize: Int = elements.length @@ -88,6 +93,10 @@ object ImmutableArray extends IterableFactory[ImmutableArray] { def fromIterable[A](it: strawman.collection.Iterable[A]): ImmutableArray[A] = new ImmutableArray(ArrayBuffer.fromIterable(it).asInstanceOf[ArrayBuffer[Any]].toArray) + def newBuilder[A](): Builder[A, ImmutableArray[A]] = + ArrayBuffer.newBuilder[A]() + .mapResult(b => new ImmutableArray[A](b.asInstanceOf[ArrayBuffer[Any]].toArray)) + override def fill[A](n: Int)(elem: => A): ImmutableArray[A] = tabulate(n)(_ => elem) def tabulate[A](n: Int)(f: Int => A): ImmutableArray[A] = { diff --git a/src/main/scala/strawman/collection/immutable/LazyList.scala b/src/main/scala/strawman/collection/immutable/LazyList.scala index b385c0b8ea..3d28495d2d 100644 --- a/src/main/scala/strawman/collection/immutable/LazyList.scala +++ b/src/main/scala/strawman/collection/immutable/LazyList.scala @@ -36,6 +36,16 @@ class LazyList[+A](expr: => LazyList.Evaluated[A]) protected[this] def fromSpecificIterable(coll: collection.Iterable[A]): LazyList[A] = fromIterable(coll) + def groupBy[K](f: A => K): immutable.Map[K, LazyList[A]] = { + val m = mutable.Map.empty[K, LazyList[A]] + for (elem <- coll) { + val key = f(elem) + val values = m.get(key).getOrElse(LazyList.Empty) + m += ((key, elem #:: values)) + } + m.to(immutable.Map) + } + override def className = "LazyList" override def toString = @@ -65,5 +75,6 @@ object LazyList extends IterableFactory[LazyList] { def fromIterator[A](it: Iterator[A]): LazyList[A] = new LazyList(if (it.hasNext) Some(it.next(), fromIterator(it)) else None) - def empty[A]: LazyList[A] = new LazyList[A](None) + def empty[A]: LazyList[A] = Empty + } diff --git a/src/main/scala/strawman/collection/immutable/ListMap.scala b/src/main/scala/strawman/collection/immutable/ListMap.scala index b8b9ca56c0..115d834850 100644 --- a/src/main/scala/strawman/collection/immutable/ListMap.scala +++ b/src/main/scala/strawman/collection/immutable/ListMap.scala @@ -16,7 +16,7 @@ import scala.annotation.tailrec import scala.{Any, AnyRef, Array, Boolean, Int, NoSuchElementException, None, Nothing, Option, SerialVersionUID, Serializable, Some, sys} import java.lang.Integer -import strawman.collection.mutable.Builder +import strawman.collection.mutable.{Builder, ImmutableBuilder} /** * This class implements immutable maps using a list-based data structure. List map iterators and @@ -45,8 +45,9 @@ import strawman.collection.mutable.Builder @SerialVersionUID(301002838095710379L) sealed class ListMap[K, +V] extends Map[K, V] - with MapOps[K, V, ListMap, ListMap[K, V]] - with Serializable { + with MapOps[K, V, ListMap, ListMap[K, V]] + with Buildable[(K, V), ListMap[K, V]] + with Serializable { def iterableFactory = List @@ -58,6 +59,8 @@ sealed class ListMap[K, +V] case _ => ListMap.fromIterable(coll) } + protected[this] def newBuilder: Builder[(K, V), ListMap[K, V]] = ListMap.newBuilder() + def empty: ListMap[K, V] = ListMap.empty[K, V] override def size: Int = 0 @@ -167,5 +170,10 @@ object ListMap extends MapFactory[ListMap] { case _ => empty ++ it } + def newBuilder[K, V](): Builder[(K, V), ListMap[K, V]] = + new ImmutableBuilder[(K, V), ListMap[K, V]](empty) { + def add(elem: (K, V)): this.type = { elems = elems + elem; this } + } + } diff --git a/src/main/scala/strawman/collection/immutable/ListSet.scala b/src/main/scala/strawman/collection/immutable/ListSet.scala index 41e1d294ba..cf3fdde5e1 100644 --- a/src/main/scala/strawman/collection/immutable/ListSet.scala +++ b/src/main/scala/strawman/collection/immutable/ListSet.scala @@ -2,7 +2,8 @@ package strawman package collection package immutable -import mutable.Builder +import mutable.{Builder, ImmutableBuilder} + import scala.annotation.tailrec import scala.{Any, Boolean, Int, NoSuchElementException, SerialVersionUID, Serializable} @@ -31,8 +32,9 @@ import scala.{Any, Boolean, Int, NoSuchElementException, SerialVersionUID, Seria @SerialVersionUID(-8417059026623606218L) sealed class ListSet[A] extends Set[A] - with SetOps[A, ListSet, ListSet[A]] - with Serializable { + with SetOps[A, ListSet, ListSet[A]] + with Buildable[A, ListSet[A]] + with Serializable { override def size: Int = 0 override def isEmpty: Boolean = true @@ -61,6 +63,7 @@ sealed class ListSet[A] def iterableFactory = ListSet protected[this] def fromSpecificIterable(coll: collection.Iterable[A]): ListSet[A] = fromIterable(coll) + protected[this] def newBuilder: Builder[A, ListSet[A]] = ListSet.newBuilder() /** * Represents an entry in the `ListSet`. @@ -124,5 +127,10 @@ object ListSet extends IterableFactory[ListSet] { def empty[A]: ListSet[A] = EmptyListSet.asInstanceOf[ListSet[A]] + def newBuilder[A](): Builder[A, ListSet[A]] = + new ImmutableBuilder[A, ListSet[A]](empty) { + def add(elem: A): this.type = { elems = elems + elem; this } + } + } diff --git a/src/main/scala/strawman/collection/immutable/NumericRange.scala b/src/main/scala/strawman/collection/immutable/NumericRange.scala index 8164a9fee4..7e5ad1d7cc 100644 --- a/src/main/scala/strawman/collection/immutable/NumericRange.scala +++ b/src/main/scala/strawman/collection/immutable/NumericRange.scala @@ -1,4 +1,5 @@ -package strawman.collection.immutable +package strawman +package collection.immutable import strawman.collection import strawman.collection.{IterableFactory, Iterator} @@ -90,6 +91,9 @@ final class NumericRange[T]( } } + def groupBy[K](f: T => K): Map[K, IndexedSeq[T]] = + collection.generic.GroupBy.strict(f, coll, () => ImmutableArray.newBuilder[T]()) + // TODO: these private methods are straight copies from Range, duplicated // to guard against any (most likely illusory) performance drop. They should // be eliminated one way or another. diff --git a/src/main/scala/strawman/collection/immutable/Range.scala b/src/main/scala/strawman/collection/immutable/Range.scala index c802876633..e33ef0d593 100644 --- a/src/main/scala/strawman/collection/immutable/Range.scala +++ b/src/main/scala/strawman/collection/immutable/Range.scala @@ -138,6 +138,9 @@ final class Range( else start + (step * idx) } + def groupBy[K](f: (Int) => K): Map[K, IndexedSeq[Int]] = + collection.generic.GroupBy.strict(f, coll, () => ImmutableArray.newBuilder[Int]()) + /*@`inline`*/ override def foreach[@specialized(Unit) U](f: Int => U): Unit = { // Implementation chosen on the basis of favorable microbenchmarks // Note--initialization catches step == 0 so we don't need to here diff --git a/src/main/scala/strawman/collection/immutable/TreeMap.scala b/src/main/scala/strawman/collection/immutable/TreeMap.scala index b22d1d76e8..1ea0d1843e 100644 --- a/src/main/scala/strawman/collection/immutable/TreeMap.scala +++ b/src/main/scala/strawman/collection/immutable/TreeMap.scala @@ -4,7 +4,7 @@ package immutable import strawman.collection.SortedMapFactory import strawman.collection.immutable.{RedBlackTree => RB} -import strawman.collection.mutable.Builder +import strawman.collection.mutable.{Builder, ImmutableBuilder} import scala.{Int, Option, Ordering, SerialVersionUID, Serializable, Some, Unit} @@ -32,6 +32,7 @@ import scala.{Int, Option, Ordering, SerialVersionUID, Serializable, Some, Unit} final class TreeMap[K, +V] private (tree: RB.Tree[K, V])(implicit val ordering: Ordering[K]) extends SortedMap[K, V] with SortedMapOps[K, V, TreeMap, TreeMap[K, V]] + with Buildable[(K, V), TreeMap[K, V]] with Serializable { def this()(implicit ordering: Ordering[K]) = this(null)(ordering) @@ -44,6 +45,8 @@ final class TreeMap[K, +V] private (tree: RB.Tree[K, V])(implicit val ordering: protected[this] def sortedMapFromIterable[K2, V2](it: collection.Iterable[(K2, V2)])(implicit ordering: Ordering[K2]): TreeMap[K2, V2] = TreeMap.sortedFromIterable(it) + protected[this] def newBuilder: Builder[(K, V), TreeMap[K, V]] = TreeMap.newBuilder() + def iterator(): collection.Iterator[(K, V)] = RB.iterator(tree) def keysIteratorFrom(start: K): collection.Iterator[K] = RB.keysIterator(tree, Some(start)) @@ -110,4 +113,9 @@ object TreeMap extends SortedMapFactory[TreeMap] { case _ => empty[K, V] ++ it } + def newBuilder[K : Ordering, V](): Builder[(K, V), TreeMap[K, V]] = + new ImmutableBuilder[(K, V), TreeMap[K, V]](empty) { + def add(elem: (K, V)): this.type = { elems = elems + elem; this } + } + } diff --git a/src/main/scala/strawman/collection/immutable/TreeSet.scala b/src/main/scala/strawman/collection/immutable/TreeSet.scala index f21ce1d508..bc684641d6 100644 --- a/src/main/scala/strawman/collection/immutable/TreeSet.scala +++ b/src/main/scala/strawman/collection/immutable/TreeSet.scala @@ -2,7 +2,7 @@ package strawman package collection package immutable -import mutable.Builder +import mutable.{Builder, ImmutableBuilder} import immutable.{RedBlackTree => RB} import scala.{Boolean, Int, NullPointerException, Option, Ordering, Some, Unit} @@ -27,12 +27,23 @@ import scala.{Boolean, Int, NullPointerException, Option, Ordering, Some, Unit} */ final class TreeSet[A] private (tree: RB.Tree[A, Unit])(implicit val ordering: Ordering[A]) extends SortedSet[A] - with SortedSetOps[A, TreeSet, TreeSet[A]] { + with SortedSetOps[A, TreeSet, TreeSet[A]] + with Buildable[A, TreeSet[A]] { if (ordering eq null) throw new NullPointerException("ordering must not be null") def this()(implicit ordering: Ordering[A]) = this(null)(ordering) + def iterableFactory = Set + + protected[this] def fromSpecificIterable(coll: strawman.collection.Iterable[A]): TreeSet[A] = + TreeSet.sortedFromIterable(coll) + + protected[this] def sortedFromIterable[B : Ordering](coll: strawman.collection.Iterable[B]): TreeSet[B] = + TreeSet.sortedFromIterable(coll) + + protected[this] def newBuilder: Builder[A, TreeSet[A]] = TreeSet.newBuilder() + private def newSet(t: RB.Tree[A, Unit]) = new TreeSet[A](t) override def size: Int = RB.count(tree) @@ -61,14 +72,6 @@ final class TreeSet[A] private (tree: RB.Tree[A, Unit])(implicit val ordering: O def keysIteratorFrom(start: A): Iterator[A] = RB.keysIterator(tree, Some(start)) - def iterableFactory = Set - - protected[this] def fromSpecificIterable(coll: strawman.collection.Iterable[A]): TreeSet[A] = - TreeSet.sortedFromIterable(coll) - - protected[this] def sortedFromIterable[B : Ordering](coll: strawman.collection.Iterable[B]): TreeSet[B] = - TreeSet.sortedFromIterable(coll) - def unordered: Set[A] = this /** Checks if this set contains element `elem`. @@ -113,4 +116,9 @@ object TreeSet extends SortedIterableFactory[TreeSet] { case _ => empty[E] ++ it } + def newBuilder[A : Ordering](): Builder[A, TreeSet[A]] = + new ImmutableBuilder[A, TreeSet[A]](empty) { + def add(elem: A): this.type = { elems = elems + elem; this } + } + } diff --git a/src/main/scala/strawman/collection/mutable/BitSet.scala b/src/main/scala/strawman/collection/mutable/BitSet.scala index 335a7e92b4..a709565c63 100644 --- a/src/main/scala/strawman/collection/mutable/BitSet.scala +++ b/src/main/scala/strawman/collection/mutable/BitSet.scala @@ -28,6 +28,7 @@ class BitSet(protected[collection] final var elems: Array[Long]) with collection.BitSet with SortedSetOps[Int, SortedSet, BitSet] with collection.BitSetOps[BitSet] + with Buildable[Int, BitSet] with Serializable { def this(initSize: Int) = this(new Array[Long](math.max((initSize + 63) >> 6, 1))) @@ -42,6 +43,8 @@ class BitSet(protected[collection] final var elems: Array[Long]) protected[this] def fromSpecificIterable(coll: collection.Iterable[Int]): BitSet = BitSet.fromSpecificIterable(coll) + protected[this] def newBuilder: Builder[Int, BitSet] = BitSet.newBuilder() + protected[collection] final def nwords: Int = elems.length protected[collection] final def word(idx: Int): Long = @@ -104,4 +107,6 @@ object BitSet extends SpecificIterableFactory[Int, BitSet] { def empty: BitSet = new BitSet() + def newBuilder(): Builder[Int, BitSet] = new GrowableBuilder[Int, BitSet](empty) + } diff --git a/src/main/scala/strawman/collection/mutable/HashMap.scala b/src/main/scala/strawman/collection/mutable/HashMap.scala index 82ccaf3028..2b3a4fecf1 100644 --- a/src/main/scala/strawman/collection/mutable/HashMap.scala +++ b/src/main/scala/strawman/collection/mutable/HashMap.scala @@ -1,7 +1,7 @@ package strawman package collection.mutable -import strawman.collection.{Iterator, MapFactory} +import strawman.collection.{Buildable, Iterator, MapFactory} import scala.{Boolean, Int, None, Option, SerialVersionUID, Serializable, Some, Unit} import java.lang.String @@ -24,6 +24,7 @@ import java.lang.String final class HashMap[K, V] private[collection] (contents: HashTable.Contents[K, DefaultEntry[K, V]]) extends Map[K, V] with MapOps[K, V, HashMap, HashMap[K, V]] + with Buildable[(K, V), HashMap[K, V]] with Serializable { private[this] val table: HashTable[K, V, DefaultEntry[K, V]] = @@ -39,6 +40,7 @@ final class HashMap[K, V] private[collection] (contents: HashTable.Contents[K, D protected[this] def fromSpecificIterable(coll: collection.Iterable[(K, V)]): HashMap[K, V] = HashMap.fromIterable(coll) protected[this] def mapFromIterable[K2, V2](it: collection.Iterable[(K2, V2)]): HashMap[K2, V2] = HashMap.fromIterable(it) + protected[this] def newBuilder: Builder[(K, V), HashMap[K, V]] = HashMap.newBuilder() def iterator(): Iterator[(K, V)] = table.entriesIterator.map(e => (e.key, e.value)) @@ -77,6 +79,22 @@ final class HashMap[K, V] private[collection] (contents: HashTable.Contents[K, D else { val v = e.value; e.value = value; Some(v) } } + override def getOrElseUpdate(key: K, defaultValue: => V): V = { + val hash = table.elemHashCode(key) + val i = table.index(hash) + val entry = table.findEntry0(key, i) + if (entry != null) entry.value + else { + val table0 = table + val default = defaultValue + // Avoid recomputing index if the `defaultValue()` hasn't triggered + // a table resize. + val newEntryIndex = if (table0 eq table) i else table.index(hash) + table.addEntry0(table.createNewEntry(key, default), newEntryIndex) + default + } + } + private def writeObject(out: java.io.ObjectOutputStream): Unit = { table.serializeTo(out, { entry => out.writeObject(entry.key) @@ -96,6 +114,8 @@ object HashMap extends MapFactory[HashMap] { def fromIterable[K, V](it: collection.Iterable[(K, V)]): HashMap[K, V] = Growable.fromIterable(empty[K, V], it) + def newBuilder[K, V](): Builder[(K, V), HashMap[K, V]] = new GrowableBuilder[(K, V), HashMap[K, V]](empty) + } /** Class used internally for default map model. diff --git a/src/main/scala/strawman/collection/mutable/HashTable.scala b/src/main/scala/strawman/collection/mutable/HashTable.scala index f32dc36bb0..28671c7fa8 100644 --- a/src/main/scala/strawman/collection/mutable/HashTable.scala +++ b/src/main/scala/strawman/collection/mutable/HashTable.scala @@ -139,7 +139,7 @@ private[mutable] abstract class HashTable[A, B, Entry >: Null <: HashEntry[A, En final def findEntry(key: A): Entry = findEntry0(key, index(elemHashCode(key))) - protected[this] final def findEntry0(key: A, h: Int): Entry = { + protected[collection] final def findEntry0(key: A, h: Int): Entry = { var e = table(h).asInstanceOf[Entry] while (e != null && !elemEquals(e.key, key)) e = e.next e @@ -152,7 +152,7 @@ private[mutable] abstract class HashTable[A, B, Entry >: Null <: HashEntry[A, En addEntry0(e, index(elemHashCode(e.key))) } - protected[this] final def addEntry0(e: Entry, h: Int): Unit = { + protected[collection] final def addEntry0(e: Entry, h: Int): Unit = { e.next = table(h).asInstanceOf[Entry] table(h) = e tableSize = tableSize + 1 @@ -363,7 +363,7 @@ private[mutable] abstract class HashTable[A, B, Entry >: Null <: HashEntry[A, En * Note: we take the most significant bits of the hashcode, not the lower ones * this is of crucial importance when populating the table in parallel */ - protected final def index(hcode: Int): Int = { + protected[collection] final def index(hcode: Int): Int = { val ones = table.length - 1 val exponent = Integer.numberOfLeadingZeros(ones) (improve(hcode, seedvalue) >>> exponent) & ones @@ -408,7 +408,7 @@ private[collection] object HashTable { // so that: protected final def sizeMapBucketSize = 1 << sizeMapBucketBitSize - protected def elemHashCode(key: KeyType) = key.## + protected[collection] def elemHashCode(key: KeyType) = key.## /** * Defer to a high-quality hash in [[scala.util.hashing]]. diff --git a/src/main/scala/strawman/collection/mutable/ImmutableBuilder.scala b/src/main/scala/strawman/collection/mutable/ImmutableBuilder.scala new file mode 100644 index 0000000000..1e61e7caca --- /dev/null +++ b/src/main/scala/strawman/collection/mutable/ImmutableBuilder.scala @@ -0,0 +1,19 @@ +package strawman +package collection +package mutable + +import scala.Unit + +/** + * Reusable builder for immutable collections + */ +abstract class ImmutableBuilder[-A, C](empty: C) + extends ReusableBuilder[A, C] { + + protected var elems: C = empty + + def clear(): Unit = { elems = empty } + + def result(): C = elems + +} \ No newline at end of file diff --git a/src/main/scala/strawman/collection/mutable/ListBuffer.scala b/src/main/scala/strawman/collection/mutable/ListBuffer.scala index 2408549c73..f388fda36b 100644 --- a/src/main/scala/strawman/collection/mutable/ListBuffer.scala +++ b/src/main/scala/strawman/collection/mutable/ListBuffer.scala @@ -12,7 +12,7 @@ import scala.Predef.{assert, intWrapper} /** Concrete collection type: ListBuffer */ class ListBuffer[A] - extends Seq[A] + extends GrowableSeq[A] with SeqOps[A, ListBuffer, ListBuffer[A]] with Buildable[A, ListBuffer[A]] with Builder[A, ListBuffer[A]] { diff --git a/src/main/scala/strawman/collection/mutable/Map.scala b/src/main/scala/strawman/collection/mutable/Map.scala index ca42d1af1f..8975b8592a 100644 --- a/src/main/scala/strawman/collection/mutable/Map.scala +++ b/src/main/scala/strawman/collection/mutable/Map.scala @@ -4,7 +4,7 @@ package mutable import strawman.collection.{IterableOnce, MapFactory} -import scala.{Boolean, Option, Unit, `inline`} +import scala.{Boolean, None, Option, Some, Unit, `inline`} /** Base type of mutable Maps */ trait Map[K, V] @@ -47,6 +47,26 @@ trait MapOps[K, V, +CC[X, Y] <: Map[X, Y], +C <: Map[K, V]] */ def update(key: K, value: V): Unit = { coll += ((key, value)) } + /** If given key is already in this map, returns associated value. + * + * Otherwise, computes value from given expression `op`, stores with key + * in map and returns that value. + * + * Concurrent map implementations may evaluate the expression `op` + * multiple times, or may evaluate `op` without inserting the result. + * + * @param key the key to test + * @param op the computation yielding the value to associate with `key`, if + * `key` is previously unbound. + * @return the value associated with key (either previously or as a result + * of executing the method). + */ + def getOrElseUpdate(key: K, op: => V): V = + get(key) match { + case Some(v) => v + case None => val d = op; this(key) = d; d + } + override def clone(): C = empty ++= coll def mapInPlace(f: ((K, V)) => (K, V)): this.type = { diff --git a/src/main/scala/strawman/collection/mutable/TreeMap.scala b/src/main/scala/strawman/collection/mutable/TreeMap.scala index 7bcf32137e..73fe2c1369 100644 --- a/src/main/scala/strawman/collection/mutable/TreeMap.scala +++ b/src/main/scala/strawman/collection/mutable/TreeMap.scala @@ -1,7 +1,7 @@ package strawman package collection.mutable -import collection.{Iterator, SortedMapFactory} +import collection.{Buildable, Iterator, SortedMapFactory} import collection.mutable.{RedBlackTree => RB} import scala.{Boolean, Int, None, Option, Ordering, SerialVersionUID, Serializable, Some, Unit} @@ -25,6 +25,7 @@ import java.lang.String sealed class TreeMap[K, V] private (tree: RB.Tree[K, V])(implicit val ordering: Ordering[K]) extends SortedMap[K, V] with SortedMapOps[K, V, TreeMap, TreeMap[K, V]] + with Buildable[(K, V), TreeMap[K, V]] with Serializable { /** @@ -38,6 +39,8 @@ sealed class TreeMap[K, V] private (tree: RB.Tree[K, V])(implicit val ordering: protected[this] def sortedMapFromIterable[K2, V2](it: collection.Iterable[(K2, V2)])(implicit ordering: Ordering[K2]): TreeMap[K2, V2] = TreeMap.sortedFromIterable(it) + protected[this] def newBuilder: Builder[(K, V), TreeMap[K, V]] = TreeMap.newBuilder() + def iterator(): Iterator[(K, V)] = RB.iterator(tree) def keysIteratorFrom(start: K): Iterator[K] = RB.keysIterator(tree, Some(start)) @@ -179,4 +182,6 @@ object TreeMap extends SortedMapFactory[TreeMap] { def empty[K : Ordering, V]: TreeMap[K, V] = new TreeMap[K, V]() + def newBuilder[K : Ordering, V](): Builder[(K, V), TreeMap[K, V]] = new GrowableBuilder[(K, V), TreeMap[K, V]](empty) + } diff --git a/src/main/scala/strawman/collection/mutable/TreeSet.scala b/src/main/scala/strawman/collection/mutable/TreeSet.scala index e6689663f9..7809b43c22 100644 --- a/src/main/scala/strawman/collection/mutable/TreeSet.scala +++ b/src/main/scala/strawman/collection/mutable/TreeSet.scala @@ -1,10 +1,10 @@ package strawman package collection.mutable -import collection.SortedIterableFactory +import collection.{Buildable, SortedIterableFactory} import collection.mutable.{RedBlackTree => RB} -import scala.{Boolean, Int, None, Null, NullPointerException, Option, Ordering, Serializable, SerialVersionUID, Some, Unit} +import scala.{Boolean, Int, None, Null, NullPointerException, Option, Ordering, SerialVersionUID, Serializable, Some, Unit} import java.lang.String /** @@ -25,6 +25,7 @@ import java.lang.String sealed class TreeSet[A] private (tree: RB.Tree[A, Null])(implicit val ordering: Ordering[A]) extends SortedSet[A] with SortedSetOps[A, TreeSet, TreeSet[A]] + with Buildable[A, TreeSet[A]] with Serializable { if (ordering eq null) @@ -43,6 +44,8 @@ sealed class TreeSet[A] private (tree: RB.Tree[A, Null])(implicit val ordering: protected[this] def fromSpecificIterable(coll: collection.Iterable[A]): TreeSet[A] = TreeSet.sortedFromIterable(coll) + protected[this] def newBuilder: Builder[A, TreeSet[A]] = TreeSet.newBuilder() + def iterableFactory = Set def keysIteratorFrom(start: A): collection.Iterator[A] = RB.keysIterator(tree, Some(start)) @@ -181,4 +184,6 @@ object TreeSet extends SortedIterableFactory[TreeSet] { def sortedFromIterable[E : Ordering](it: collection.Iterable[E]): TreeSet[E] = Growable.fromIterable(empty[E], it) + def newBuilder[A : Ordering](): Builder[A, TreeSet[A]] = new GrowableBuilder[A, TreeSet[A]](empty) + }