|
| 1 | +/* |
| 2 | + * Scala (https://www.scala-lang.org) |
| 3 | + * |
| 4 | + * Copyright EPFL and Lightbend, Inc. |
| 5 | + * |
| 6 | + * Licensed under Apache License 2.0 |
| 7 | + * (http://www.apache.org/licenses/LICENSE-2.0). |
| 8 | + * |
| 9 | + * See the NOTICE file distributed with this work for |
| 10 | + * additional information regarding copyright ownership. |
| 11 | + */ |
| 12 | + |
| 13 | +package scala |
| 14 | +package collection |
| 15 | + |
| 16 | +import scala.annotation.nowarn |
| 17 | +import scala.annotation.unchecked.uncheckedVariance |
| 18 | +import scala.runtime.Statics |
| 19 | +import language.experimental.captureChecking |
| 20 | + |
| 21 | +/** |
| 22 | + * Trait that overrides iterable operations to take advantage of strict builders. |
| 23 | + * |
| 24 | + * @tparam A Elements type |
| 25 | + * @tparam CC Collection type constructor |
| 26 | + * @tparam C Collection type |
| 27 | + */ |
| 28 | +trait StrictOptimizedIterableOps[+A, +CC[_], +C] |
| 29 | + extends Any |
| 30 | + with IterableOps[A, CC, C] { |
| 31 | + this: StrictOptimizedIterableOps[A, CC, C] => |
| 32 | + |
| 33 | + // Optimized, push-based version of `partition` |
| 34 | + override def partition(p: A => Boolean): (C, C) = { |
| 35 | + val l, r = newSpecificBuilder |
| 36 | + iterator.foreach(x => (if (p(x)) l else r) += x) |
| 37 | + (l.result(), r.result()) |
| 38 | + } |
| 39 | + |
| 40 | + override def span(p: A => Boolean): (C, C) = { |
| 41 | + val first = newSpecificBuilder |
| 42 | + val second = newSpecificBuilder |
| 43 | + val it = iterator |
| 44 | + var inFirst = true |
| 45 | + while (it.hasNext && inFirst) { |
| 46 | + val a = it.next() |
| 47 | + if (p(a)) { |
| 48 | + first += a |
| 49 | + } else { |
| 50 | + second += a |
| 51 | + inFirst = false |
| 52 | + } |
| 53 | + } |
| 54 | + while (it.hasNext) { |
| 55 | + second += it.next() |
| 56 | + } |
| 57 | + (first.result(), second.result()) |
| 58 | + } |
| 59 | + |
| 60 | + override def unzip[A1, A2](implicit asPair: A -> (A1, A2)): (CC[A1], CC[A2]) = { |
| 61 | + val first = iterableFactory.newBuilder[A1] |
| 62 | + val second = iterableFactory.newBuilder[A2] |
| 63 | + foreach { a => |
| 64 | + val pair = asPair(a) |
| 65 | + first += pair._1 |
| 66 | + second += pair._2 |
| 67 | + } |
| 68 | + (first.result(), second.result()) |
| 69 | + } |
| 70 | + |
| 71 | + override def unzip3[A1, A2, A3](implicit asTriple: A -> (A1, A2, A3)): (CC[A1], CC[A2], CC[A3]) = { |
| 72 | + val b1 = iterableFactory.newBuilder[A1] |
| 73 | + val b2 = iterableFactory.newBuilder[A2] |
| 74 | + val b3 = iterableFactory.newBuilder[A3] |
| 75 | + |
| 76 | + foreach { xyz => |
| 77 | + val triple = asTriple(xyz) |
| 78 | + b1 += triple._1 |
| 79 | + b2 += triple._2 |
| 80 | + b3 += triple._3 |
| 81 | + } |
| 82 | + (b1.result(), b2.result(), b3.result()) |
| 83 | + } |
| 84 | + |
| 85 | + // The implementations of the following operations are not fundamentally different from |
| 86 | + // the view-based implementations, but they turn out to be slightly faster because |
| 87 | + // a couple of indirection levels are removed |
| 88 | + |
| 89 | + override def map[B](f: A => B): CC[B] = |
| 90 | + strictOptimizedMap(iterableFactory.newBuilder, f) |
| 91 | + |
| 92 | + /** |
| 93 | + * @param b Builder to use to build the resulting collection |
| 94 | + * @param f Element transformation function |
| 95 | + * @tparam B Type of elements of the resulting collection (e.g. `String`) |
| 96 | + * @tparam C2 Type of the resulting collection (e.g. `List[String]`) |
| 97 | + * @return The resulting collection |
| 98 | + */ |
| 99 | + @inline protected[this] final def strictOptimizedMap[B, C2](b: mutable.Builder[B, C2], f: A => B): C2 = { |
| 100 | + val it = iterator |
| 101 | + while (it.hasNext) { |
| 102 | + b += f(it.next()) |
| 103 | + } |
| 104 | + b.result() |
| 105 | + } |
| 106 | + |
| 107 | + override def flatMap[B](f: A => IterableOnce[B]^): CC[B] = |
| 108 | + strictOptimizedFlatMap(iterableFactory.newBuilder, f) |
| 109 | + |
| 110 | + /** |
| 111 | + * @param b Builder to use to build the resulting collection |
| 112 | + * @param f Element transformation function |
| 113 | + * @tparam B Type of elements of the resulting collection (e.g. `String`) |
| 114 | + * @tparam C2 Type of the resulting collection (e.g. `List[String]`) |
| 115 | + * @return The resulting collection |
| 116 | + */ |
| 117 | + @inline protected[this] final def strictOptimizedFlatMap[B, C2](b: mutable.Builder[B, C2], f: A => IterableOnce[B]^): C2 = { |
| 118 | + val it = iterator |
| 119 | + while (it.hasNext) { |
| 120 | + b ++= f(it.next()) |
| 121 | + } |
| 122 | + b.result() |
| 123 | + } |
| 124 | + |
| 125 | + /** |
| 126 | + * @param that Elements to concatenate to this collection |
| 127 | + * @param b Builder to use to build the resulting collection |
| 128 | + * @tparam B Type of elements of the resulting collections (e.g. `Int`) |
| 129 | + * @tparam C2 Type of the resulting collection (e.g. `List[Int]`) |
| 130 | + * @return The resulting collection |
| 131 | + */ |
| 132 | + @inline protected[this] final def strictOptimizedConcat[B >: A, C2](that: IterableOnce[B]^, b: mutable.Builder[B, C2]): C2 = { |
| 133 | + b ++= this |
| 134 | + b ++= that |
| 135 | + b.result() |
| 136 | + } |
| 137 | + |
| 138 | + override def collect[B](pf: PartialFunction[A, B]^): CC[B] = |
| 139 | + strictOptimizedCollect(iterableFactory.newBuilder, pf) |
| 140 | + |
| 141 | + /** |
| 142 | + * @param b Builder to use to build the resulting collection |
| 143 | + * @param pf Element transformation partial function |
| 144 | + * @tparam B Type of elements of the resulting collection (e.g. `String`) |
| 145 | + * @tparam C2 Type of the resulting collection (e.g. `List[String]`) |
| 146 | + * @return The resulting collection |
| 147 | + */ |
| 148 | + @inline protected[this] final def strictOptimizedCollect[B, C2](b: mutable.Builder[B, C2], pf: PartialFunction[A, B]^): C2 = { |
| 149 | + val marker = Statics.pfMarker |
| 150 | + val it = iterator |
| 151 | + while (it.hasNext) { |
| 152 | + val elem = it.next() |
| 153 | + val v = pf.applyOrElse(elem, ((x: A) => marker).asInstanceOf[Function[A, B]]) |
| 154 | + if (marker ne v.asInstanceOf[AnyRef]) b += v |
| 155 | + } |
| 156 | + b.result() |
| 157 | + } |
| 158 | + |
| 159 | + override def flatten[B](implicit toIterableOnce: A -> IterableOnce[B]): CC[B] = |
| 160 | + strictOptimizedFlatten(iterableFactory.newBuilder) |
| 161 | + |
| 162 | + /** |
| 163 | + * @param b Builder to use to build the resulting collection |
| 164 | + * @param toIterableOnce Evidence that `A` can be seen as an `IterableOnce[B]` |
| 165 | + * @tparam B Type of elements of the resulting collection (e.g. `Int`) |
| 166 | + * @tparam C2 Type of the resulting collection (e.g. `List[Int]`) |
| 167 | + * @return The resulting collection |
| 168 | + */ |
| 169 | + @inline protected[this] final def strictOptimizedFlatten[B, C2](b: mutable.Builder[B, C2])(implicit toIterableOnce: A -> IterableOnce[B]): C2 = { |
| 170 | + val it = iterator |
| 171 | + while (it.hasNext) { |
| 172 | + b ++= toIterableOnce(it.next()) |
| 173 | + } |
| 174 | + b.result() |
| 175 | + } |
| 176 | + |
| 177 | + override def zip[B](that: IterableOnce[B]^): CC[(A @uncheckedVariance, B)] = |
| 178 | + strictOptimizedZip(that, iterableFactory.newBuilder[(A, B)]) |
| 179 | + |
| 180 | + /** |
| 181 | + * @param that Collection to zip with this collection |
| 182 | + * @param b Builder to use to build the resulting collection |
| 183 | + * @tparam B Type of elements of the second collection (e.g. `String`) |
| 184 | + * @tparam C2 Type of the resulting collection (e.g. `List[(Int, String)]`) |
| 185 | + * @return The resulting collection |
| 186 | + */ |
| 187 | + @inline protected[this] final def strictOptimizedZip[B, C2](that: IterableOnce[B]^, b: mutable.Builder[(A, B), C2]): C2 = { |
| 188 | + val it1 = iterator |
| 189 | + val it2 = that.iterator |
| 190 | + while (it1.hasNext && it2.hasNext) { |
| 191 | + b += ((it1.next(), it2.next())) |
| 192 | + } |
| 193 | + b.result() |
| 194 | + } |
| 195 | + |
| 196 | + override def zipWithIndex: CC[(A @uncheckedVariance, Int)] = { |
| 197 | + val b = iterableFactory.newBuilder[(A, Int)] |
| 198 | + var i = 0 |
| 199 | + val it = iterator |
| 200 | + while (it.hasNext) { |
| 201 | + b += ((it.next(), i)) |
| 202 | + i += 1 |
| 203 | + } |
| 204 | + b.result() |
| 205 | + } |
| 206 | + |
| 207 | + override def scanLeft[B](z: B)(op: (B, A) => B): CC[B] = { |
| 208 | + val b = iterableFactory.newBuilder[B] |
| 209 | + b.sizeHint(this, delta = 0) |
| 210 | + var acc = z |
| 211 | + b += acc |
| 212 | + val it = iterator |
| 213 | + while (it.hasNext) { |
| 214 | + acc = op(acc, it.next()) |
| 215 | + b += acc |
| 216 | + } |
| 217 | + b.result() |
| 218 | + } |
| 219 | + |
| 220 | + override def filter(pred: A => Boolean): C = filterImpl(pred, isFlipped = false) |
| 221 | + |
| 222 | + override def filterNot(pred: A => Boolean): C = filterImpl(pred, isFlipped = true) |
| 223 | + |
| 224 | + protected[collection] def filterImpl(pred: A => Boolean, isFlipped: Boolean): C = { |
| 225 | + val b = newSpecificBuilder |
| 226 | + val it = iterator |
| 227 | + while (it.hasNext) { |
| 228 | + val elem = it.next() |
| 229 | + if (pred(elem) != isFlipped) { |
| 230 | + b += elem |
| 231 | + } |
| 232 | + } |
| 233 | + b.result() |
| 234 | + } |
| 235 | + |
| 236 | + // Optimized, push-based version of `partitionMap` |
| 237 | + override def partitionMap[A1, A2](f: A => Either[A1, A2]): (CC[A1], CC[A2]) = { |
| 238 | + val l = iterableFactory.newBuilder[A1] |
| 239 | + val r = iterableFactory.newBuilder[A2] |
| 240 | + foreach { x => |
| 241 | + f(x) match { |
| 242 | + case Left(x1) => l += x1 |
| 243 | + case Right(x2) => r += x2 |
| 244 | + } |
| 245 | + } |
| 246 | + (l.result(), r.result()) |
| 247 | + } |
| 248 | + |
| 249 | + // Optimization avoids creation of second collection |
| 250 | + override def tapEach[U](f: A => U): C = { |
| 251 | + foreach(f) |
| 252 | + coll |
| 253 | + } |
| 254 | + |
| 255 | + /** A collection containing the last `n` elements of this collection. |
| 256 | + * $willForceEvaluation |
| 257 | + */ |
| 258 | + override def takeRight(n: Int): C = { |
| 259 | + val b = newSpecificBuilder |
| 260 | + b.sizeHintBounded(n, toIterable: @nowarn("cat=deprecation")) |
| 261 | + val lead = iterator drop n |
| 262 | + val it = iterator |
| 263 | + while (lead.hasNext) { |
| 264 | + lead.next() |
| 265 | + it.next() |
| 266 | + } |
| 267 | + while (it.hasNext) b += it.next() |
| 268 | + b.result() |
| 269 | + } |
| 270 | + |
| 271 | + /** The rest of the collection without its `n` last elements. For |
| 272 | + * linear, immutable collections this should avoid making a copy. |
| 273 | + * $willForceEvaluation |
| 274 | + */ |
| 275 | + override def dropRight(n: Int): C = { |
| 276 | + val b = newSpecificBuilder |
| 277 | + if (n >= 0) b.sizeHint(this, delta = -n) |
| 278 | + val lead = iterator drop n |
| 279 | + val it = iterator |
| 280 | + while (lead.hasNext) { |
| 281 | + b += it.next() |
| 282 | + lead.next() |
| 283 | + } |
| 284 | + b.result() |
| 285 | + } |
| 286 | +} |
0 commit comments