diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index 2b962e94e148..cc25965945d0 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -381,8 +381,8 @@ final class Vector[+A] private[immutable] (private[collection] val startIndex: I override def appended[B >: A](value: B): Vector[B] = { val result = if (endIndex != startIndex) { - val blockIndex = endIndex & ~31 - val lo = endIndex & 31 + val blockIndex = endIndex & ~31 // round down to nearest 32 + val lo = endIndex & 31 // remainder of blockIndex / 32 if (endIndex != blockIndex) { val s = new Vector(startIndex, endIndex + 1, blockIndex) @@ -616,6 +616,8 @@ final class Vector[+A] private[immutable] (private[collection] val startIndex: I case _ => super.equals(o) } + override def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Int = iterator.copyToArray(xs, start, len) + override def toVector: Vector[A] = this override protected[this] def className = "Vector" @@ -628,12 +630,27 @@ class VectorIterator[+A](_startIndex: Int, endIndex: Int) private[this] var blockIndex: Int = _startIndex & ~31 private[this] var lo: Int = _startIndex & 31 - private[this] var endLo = math.min(endIndex - blockIndex, 32) + private[this] var endLo = Math.min(endIndex - blockIndex, 32) def hasNext = _hasNext private[this] var _hasNext = blockIndex + lo < endIndex + private[this] def advanceToNextBlockIfNecessary(): Unit = { + if (lo == endLo) { + if (blockIndex + lo < endIndex) { + val newBlockIndex = blockIndex + 32 + gotoNextBlockStart(newBlockIndex, blockIndex ^ newBlockIndex) + + blockIndex = newBlockIndex + endLo = Math.min(endIndex - blockIndex, 32) + lo = 0 + } else { + _hasNext = false + } + } + } + override def drop(n: Int): Iterator[A] = { if (n > 0) { val longLo = lo.toLong + n @@ -657,24 +674,25 @@ class VectorIterator[+A](_startIndex: Int, endIndex: Int) def next(): A = { if (!_hasNext) throw new NoSuchElementException("reached iterator end") - val res = display0(lo).asInstanceOf[A] lo += 1 + advanceToNextBlockIfNecessary() + res + } - if (lo == endLo) { - if (blockIndex + lo < endIndex) { - val newBlockIndex = blockIndex + 32 - gotoNextBlockStart(newBlockIndex, blockIndex ^ newBlockIndex) - - blockIndex = newBlockIndex - endLo = math.min(endIndex - blockIndex, 32) - lo = 0 - } else { - _hasNext = false - } + override def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Int = { + val xsLen = xs.length + val totalToBeCopied = IterableOnce.elemsToCopyToArray(remainingElementCount, xsLen, start, len) + var totalCopied = 0 + while (hasNext && totalCopied < totalToBeCopied) { + val _start = start + totalCopied + val toBeCopied = IterableOnce.elemsToCopyToArray(endLo - lo, xsLen, _start, len - totalCopied) + Array.copy(display0, lo, xs, _start, toBeCopied) + totalCopied += toBeCopied + lo += toBeCopied + advanceToNextBlockIfNecessary() } - - res + totalCopied } private[collection] def remainingElementCount: Int = (endIndex - (blockIndex + lo)) max 0 @@ -707,18 +725,31 @@ final class VectorBuilder[A]() extends ReusableBuilder[A, Vector[A]] with Vector def isEmpty: Boolean = size == 0 def nonEmpty: Boolean = size != 0 - def addOne(elem: A): this.type = { + private[this] def advanceToNextBlockIfNecessary(): Unit = { if (lo >= display0.length) { val newBlockIndex = blockIndex + 32 gotoNextBlockStartWritable(newBlockIndex, blockIndex ^ newBlockIndex) blockIndex = newBlockIndex lo = 0 } + } + + def addOne(elem: A): this.type = { + advanceToNextBlockIfNecessary() display0(lo) = elem.asInstanceOf[AnyRef] lo += 1 this } + override def addAll(xs: IterableOnce[A]): this.type = { + val it = (xs.iterator : Iterator[A]).asInstanceOf[Iterator[AnyRef]] + while (it.hasNext) { + advanceToNextBlockIfNecessary() + lo += it.copyToArray(xs = display0, start = lo, len = display0.length - lo) + } + this + } + def result(): Vector[A] = { val size = this.size if (size == 0) diff --git a/test/benchmarks/src/main/scala/scala/collection/immutable/VectorBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorBenchmark.scala new file mode 100644 index 000000000000..bf095c44c04c --- /dev/null +++ b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorBenchmark.scala @@ -0,0 +1,28 @@ +package scala.collection.immutable + +import java.util.concurrent.TimeUnit + +import org.openjdk.jmh.infra.Blackhole + +import org.openjdk.jmh.annotations._ + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(2) +@Threads(1) +@Warmup(iterations = 10) +@Measurement(iterations = 10) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class VectorBenchmark { + @Param(Array("0", "10", "1000", "1000000")) + var size: Int = _ + var vec: Vector[AnyRef] = _ + val array = Array.fill(1000000)(new AnyRef) + + @Setup(Level.Trial) def initKeys(): Unit = { + vec = Vector.fill(size)(new AnyRef) + } + @Benchmark def concat(bh: Blackhole): Any = { + bh.consume(vec.copyToArray(array, 0, size)) + } +} diff --git a/test/junit/scala/collection/immutable/VectorTest.scala b/test/junit/scala/collection/immutable/VectorTest.scala index f274ca58f4fc..27203e473854 100644 --- a/test/junit/scala/collection/immutable/VectorTest.scala +++ b/test/junit/scala/collection/immutable/VectorTest.scala @@ -63,6 +63,18 @@ class VectorTest { assertEquals(Vector(1, 2, 3, 0), Vector(0).prependedAll(i)) } + @Test + def concat: Unit = { + assertEquals((1 to 100).toVector, (1 to 7).toVector concat (8 to 100).toVector) + } + + @Test + def copyToArray: Unit = { + val array = Array.fill(100)(2) + Vector.fill(100)(1).copyToArray(array, 0, 100) + assertEquals(array.toSeq, Seq.fill(100)(1)) + } + @Test def vectorIteratorDrop(): Unit = { val underlying = Vector(0 to 10010: _*)