Skip to content

Commit f97da22

Browse files
authored
Merge pull request scala/scala#9091 from mkeskells/2.12.x_array_copy
Small optimisations - some faster array copies, reuse empty arrays
2 parents 6b21347 + 60a1981 commit f97da22

File tree

7 files changed

+106
-80
lines changed

7 files changed

+106
-80
lines changed

library/src/scala/Array.scala

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,19 @@ class FallbackArrayBuilding {
5151
* @since 1.0
5252
*/
5353
object Array extends FallbackArrayBuilding {
54-
val emptyBooleanArray = new Array[Boolean](0)
55-
val emptyByteArray = new Array[Byte](0)
56-
val emptyCharArray = new Array[Char](0)
57-
val emptyDoubleArray = new Array[Double](0)
58-
val emptyFloatArray = new Array[Float](0)
59-
val emptyIntArray = new Array[Int](0)
60-
val emptyLongArray = new Array[Long](0)
61-
val emptyShortArray = new Array[Short](0)
62-
val emptyObjectArray = new Array[Object](0)
54+
55+
val emptyBooleanArray = empty[Boolean]
56+
val emptyByteArray = empty[Byte]
57+
val emptyCharArray = empty[Char]
58+
val emptyDoubleArray = empty[Double]
59+
val emptyFloatArray = empty[Float]
60+
val emptyIntArray = empty[Int]
61+
val emptyLongArray = empty[Long]
62+
val emptyShortArray = empty[Short]
63+
64+
private[scala] //this is only private because of binary compatability
65+
val emptyUnitArray = empty[scala.runtime.BoxedUnit].asInstanceOf[Array[Unit]]
66+
val emptyObjectArray = empty[Object]
6367

6468
implicit def canBuildFrom[T](implicit tag: ClassTag[T]): CanBuildFrom[Array[_], T, Array[T]] = {
6569
val cls = tag.runtimeClass
@@ -179,8 +183,9 @@ object Array extends FallbackArrayBuilding {
179183
}
180184

181185
/** Returns an array of length 0 */
182-
def empty[T: ClassTag]: Array[T] = new Array[T](0)
183-
186+
def empty[T: ClassTag]: Array[T] = {
187+
implicitly[ClassTag[T]].emptyArray
188+
}
184189
/** Creates an array with given elements.
185190
*
186191
* @param xs the elements to put in the array

library/src/scala/collection/concurrent/TrieMap.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -487,9 +487,7 @@ private[collection] final class CNode[K, V](val bitmap: Int, val array: Array[Ba
487487
}
488488

489489
def updatedAt(pos: Int, nn: BasicNode, gen: Gen) = {
490-
val len = array.length
491-
val narr = new Array[BasicNode](len)
492-
Array.copy(array, 0, narr, 0, len)
490+
val narr = java.util.Arrays.copyOf(array, array.length)
493491
narr(pos) = nn
494492
new CNode[K, V](bitmap, narr, gen)
495493
}

library/src/scala/collection/mutable/ArrayBuilder.scala

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,9 @@ object ArrayBuilder {
133133
private var size: Int = 0
134134

135135
private def mkArray(size: Int): Array[Byte] = {
136-
val newelems = new Array[Byte](size)
137-
if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size)
138-
newelems
136+
if (size == 0) Array.emptyByteArray
137+
else if (elems eq null) new Array(size)
138+
else java.util.Arrays.copyOf(elems, size)
139139
}
140140

141141
private def resize(size: Int) {
@@ -198,9 +198,9 @@ object ArrayBuilder {
198198
private var size: Int = 0
199199

200200
private def mkArray(size: Int): Array[Short] = {
201-
val newelems = new Array[Short](size)
202-
if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size)
203-
newelems
201+
if (size == 0) Array.emptyShortArray
202+
else if (elems eq null) new Array(size)
203+
else java.util.Arrays.copyOf(elems, size)
204204
}
205205

206206
private def resize(size: Int) {
@@ -263,9 +263,9 @@ object ArrayBuilder {
263263
private var size: Int = 0
264264

265265
private def mkArray(size: Int): Array[Char] = {
266-
val newelems = new Array[Char](size)
267-
if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size)
268-
newelems
266+
if (size == 0) Array.emptyCharArray
267+
else if (elems eq null) new Array(size)
268+
else java.util.Arrays.copyOf(elems, size)
269269
}
270270

271271
private def resize(size: Int) {
@@ -328,9 +328,9 @@ object ArrayBuilder {
328328
private var size: Int = 0
329329

330330
private def mkArray(size: Int): Array[Int] = {
331-
val newelems = new Array[Int](size)
332-
if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size)
333-
newelems
331+
if (size == 0) Array.emptyIntArray
332+
else if (elems eq null) new Array(size)
333+
else java.util.Arrays.copyOf(elems, size)
334334
}
335335

336336
private def resize(size: Int) {
@@ -393,9 +393,9 @@ object ArrayBuilder {
393393
private var size: Int = 0
394394

395395
private def mkArray(size: Int): Array[Long] = {
396-
val newelems = new Array[Long](size)
397-
if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size)
398-
newelems
396+
if (size == 0) Array.emptyLongArray
397+
else if (elems eq null) new Array(size)
398+
else java.util.Arrays.copyOf(elems, size)
399399
}
400400

401401
private def resize(size: Int) {
@@ -458,9 +458,9 @@ object ArrayBuilder {
458458
private var size: Int = 0
459459

460460
private def mkArray(size: Int): Array[Float] = {
461-
val newelems = new Array[Float](size)
462-
if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size)
463-
newelems
461+
if (size == 0) Array.emptyFloatArray
462+
else if (elems eq null) new Array(size)
463+
else java.util.Arrays.copyOf(elems, size)
464464
}
465465

466466
private def resize(size: Int) {
@@ -523,9 +523,9 @@ object ArrayBuilder {
523523
private var size: Int = 0
524524

525525
private def mkArray(size: Int): Array[Double] = {
526-
val newelems = new Array[Double](size)
527-
if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size)
528-
newelems
526+
if (size == 0) Array.emptyDoubleArray
527+
else if (elems eq null) new Array(size)
528+
else java.util.Arrays.copyOf(elems, size)
529529
}
530530

531531
private def resize(size: Int) {
@@ -588,9 +588,9 @@ object ArrayBuilder {
588588
private var size: Int = 0
589589

590590
private def mkArray(size: Int): Array[Boolean] = {
591-
val newelems = new Array[Boolean](size)
592-
if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size)
593-
newelems
591+
if (size == 0) Array.emptyBooleanArray
592+
else if (elems eq null) new Array(size)
593+
else java.util.Arrays.copyOf(elems, size)
594594
}
595595

596596
private def resize(size: Int) {
@@ -663,10 +663,12 @@ object ArrayBuilder {
663663
def clear() { size = 0 }
664664

665665
def result() = {
666-
val ans = new Array[Unit](size)
667-
var i = 0
668-
while (i < size) { ans(i) = (); i += 1 }
669-
ans
666+
if (size == 0) Array.emptyUnitArray
667+
else {
668+
val ans = new Array[Unit](size)
669+
java.util.Arrays.fill(ans.asInstanceOf[Array[AnyRef]], ())
670+
ans
671+
}
670672
}
671673

672674
override def equals(other: Any): Boolean = other match {

library/src/scala/collection/mutable/BitSet.scala

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,7 @@ class BitSet(protected final var elems: Array[Long]) extends AbstractSet[Int]
7373
if (idx >= nwords) {
7474
var newlen = nwords
7575
while (idx >= newlen) newlen = (newlen * 2) min MaxSize
76-
val elems1 = new Array[Long](newlen)
77-
Array.copy(elems, 0, elems1, 0, nwords)
78-
elems = elems1
76+
elems = java.util.Arrays.copyOf(elems, newlen)
7977
}
8078
}
8179

@@ -175,11 +173,8 @@ class BitSet(protected final var elems: Array[Long]) extends AbstractSet[Int]
175173
"immutability of the result.", "2.12.0")
176174
def toImmutable = immutable.BitSet.fromBitMaskNoCopy(elems)
177175

178-
override def clone(): BitSet = {
179-
val elems1 = new Array[Long](elems.length)
180-
Array.copy(elems, 0, elems1, 0, elems.length)
181-
new BitSet(elems1)
182-
}
176+
override def clone(): BitSet =
177+
new BitSet(elems.clone)
183178
}
184179

185180
/** $factoryInfo
@@ -198,13 +193,8 @@ object BitSet extends BitSetFactory[BitSet] {
198193
/** A bitset containing all the bits in an array */
199194
def fromBitMask(elems: Array[Long]): BitSet = {
200195
val len = elems.length
201-
if (len == 0) {
202-
empty
203-
} else {
204-
val a = new Array[Long](len)
205-
Array.copy(elems, 0, a, 0, len)
206-
new BitSet(a)
207-
}
196+
if (len == 0) empty
197+
else new BitSet(elems.clone)
208198
}
209199

210200
/** A bitset containing all the bits in an array, wrapping the existing

library/src/scala/collection/mutable/WrappedArrayBuilder.scala

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,45 @@ class WrappedArrayBuilder[A](tag: ClassTag[A]) extends ReusableBuilder[A, Wrappe
3535
private var size: Int = 0
3636

3737
private def mkArray(size: Int): WrappedArray[A] = {
38-
val runtimeClass = tag.runtimeClass
39-
val newelems = if (runtimeClass.isPrimitive) {
40-
runtimeClass match {
41-
case java.lang.Integer.TYPE => new WrappedArray.ofInt(new Array[Int](size)).asInstanceOf[WrappedArray[A]]
42-
case java.lang.Double.TYPE => new WrappedArray.ofDouble(new Array[Double](size)).asInstanceOf[WrappedArray[A]]
43-
case java.lang.Long.TYPE => new WrappedArray.ofLong(new Array[Long](size)).asInstanceOf[WrappedArray[A]]
44-
case java.lang.Float.TYPE => new WrappedArray.ofFloat(new Array[Float](size)).asInstanceOf[WrappedArray[A]]
45-
case java.lang.Character.TYPE => new WrappedArray.ofChar(new Array[Char](size)).asInstanceOf[WrappedArray[A]]
46-
case java.lang.Byte.TYPE => new WrappedArray.ofByte(new Array[Byte](size)).asInstanceOf[WrappedArray[A]]
47-
case java.lang.Short.TYPE => new WrappedArray.ofShort(new Array[Short](size)).asInstanceOf[WrappedArray[A]]
48-
case java.lang.Boolean.TYPE => new WrappedArray.ofBoolean(new Array[Boolean](size)).asInstanceOf[WrappedArray[A]]
49-
case java.lang.Void.TYPE => new WrappedArray.ofUnit(new Array[Unit](size)).asInstanceOf[WrappedArray[A]]
38+
if (size == 0) tag.emptyWrappedArray
39+
else {
40+
import java.util.Arrays.copyOf
41+
val runtimeClass = tag.runtimeClass
42+
if (runtimeClass.isPrimitive)
43+
runtimeClass match {
44+
case java.lang.Integer.TYPE =>
45+
val array = if (elems eq null) new Array[Int](size) else copyOf(elems.array.asInstanceOf[Array[Int]], size)
46+
new WrappedArray.ofInt(array).asInstanceOf[WrappedArray[A]]
47+
case java.lang.Double.TYPE =>
48+
val array = if (elems eq null) new Array[Double](size) else copyOf(elems.array.asInstanceOf[Array[Double]], size)
49+
new WrappedArray.ofDouble(array).asInstanceOf[WrappedArray[A]]
50+
case java.lang.Long.TYPE =>
51+
val array = if (elems eq null) new Array[Long](size) else copyOf(elems.array.asInstanceOf[Array[Long]], size)
52+
new WrappedArray.ofLong(array).asInstanceOf[WrappedArray[A]]
53+
case java.lang.Float.TYPE =>
54+
val array = if (elems eq null) new Array[Float](size) else copyOf(elems.array.asInstanceOf[Array[Float]], size)
55+
new WrappedArray.ofFloat(array).asInstanceOf[WrappedArray[A]]
56+
case java.lang.Character.TYPE =>
57+
val array = if (elems eq null) new Array[Char](size) else copyOf(elems.array.asInstanceOf[Array[Char]], size)
58+
new WrappedArray.ofChar(array).asInstanceOf[WrappedArray[A]]
59+
case java.lang.Byte.TYPE =>
60+
val array = if (elems eq null) new Array[Byte](size) else copyOf(elems.array.asInstanceOf[Array[Byte]], size)
61+
new WrappedArray.ofByte(array).asInstanceOf[WrappedArray[A]]
62+
case java.lang.Short.TYPE =>
63+
val array = if (elems eq null) new Array[Short](size) else copyOf(elems.array.asInstanceOf[Array[Short]], size)
64+
new WrappedArray.ofShort(array).asInstanceOf[WrappedArray[A]]
65+
case java.lang.Boolean.TYPE =>
66+
val array = if (elems eq null) new Array[Boolean](size) else copyOf(elems.array.asInstanceOf[Array[Boolean]], size)
67+
new WrappedArray.ofBoolean(array).asInstanceOf[WrappedArray[A]]
68+
case java.lang.Void.TYPE =>
69+
val array = if (elems eq null) new Array[Unit](size) else copyOf(elems.array.asInstanceOf[Array[AnyRef]], size).asInstanceOf[Array[Unit]]
70+
new WrappedArray.ofUnit(array).asInstanceOf[WrappedArray[A]]
71+
}
72+
else {
73+
val array = if (elems eq null) new Array[A with AnyRef](size) else copyOf(elems.array.asInstanceOf[Array[A with AnyRef]], size)
74+
new WrappedArray.ofRef(array).asInstanceOf[WrappedArray[A]]
5075
}
51-
} else {
52-
new WrappedArray.ofRef[A with AnyRef](tag.newArray(size).asInstanceOf[Array[A with AnyRef]]).asInstanceOf[WrappedArray[A]]
5376
}
54-
if (this.size > 0) Array.copy(elems.array, 0, newelems.array, 0, this.size)
55-
newelems
5677
}
5778

5879
private def resize(size: Int) {
@@ -90,4 +111,4 @@ class WrappedArrayBuilder[A](tag: ClassTag[A]) extends ReusableBuilder[A, Wrappe
90111
}
91112

92113
// todo: add ++=
93-
}
114+
}

library/src/scala/collection/parallel/mutable/package.scala

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,8 @@ package mutable {
6565
def internalArray = array
6666
def setInternalSize(s: Int) = size0 = s
6767
override def sizeHint(len: Int) = {
68-
if (len > size && len >= 1) {
69-
val newarray = new Array[AnyRef](len)
70-
Array.copy(array, 0, newarray, 0, size0)
71-
array = newarray
72-
}
68+
if (len > size && len >= 1)
69+
java.util.Arrays.copyOf(array, len)
7370
}
7471
}
7572

library/src/scala/reflect/ClassTag.scala

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ package reflect
1515

1616
import java.lang.{ Class => jClass }
1717

18+
import scala.collection.mutable
19+
import scala.runtime.BoxedUnit
20+
1821
/**
1922
*
2023
* A `ClassTag[T]` stores the erased class of a given type `T`, accessible via the `runtimeClass`
@@ -46,6 +49,15 @@ import java.lang.{ Class => jClass }
4649
*/
4750
@scala.annotation.implicitNotFound(msg = "No ClassTag available for ${T}")
4851
trait ClassTag[T] extends ClassManifestDeprecatedApis[T] with Equals with Serializable {
52+
53+
@transient private[scala] lazy val emptyArray : Array[T] = {
54+
val componentType =
55+
if (runtimeClass eq classOf[Void]) classOf[BoxedUnit] else runtimeClass
56+
java.lang.reflect.Array.newInstance(componentType, 0).asInstanceOf[Array[T]]
57+
}
58+
@transient private[scala] lazy val emptyWrappedArray: mutable.WrappedArray[T] =
59+
mutable.WrappedArray.make[T](emptyArray)
60+
4961
// please, don't add any APIs here, like it was with `newWrappedArray` and `newArrayBuilder`
5062
// class tags, and all tags in general, should be as minimalistic as possible
5163

@@ -151,7 +163,8 @@ object ClassTag {
151163
@SerialVersionUID(1L)
152164
private class GenericClassTag[T](val runtimeClass: jClass[_]) extends ClassTag[T] {
153165
override def newArray(len: Int): Array[T] = {
154-
java.lang.reflect.Array.newInstance(runtimeClass, len).asInstanceOf[Array[T]]
166+
if (len == 0) emptyArray
167+
else java.lang.reflect.Array.newInstance(runtimeClass, len).asInstanceOf[Array[T]]
155168
}
156169
}
157170

0 commit comments

Comments
 (0)