You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spark.apache.org by ur...@apache.org on 2026/08/25 15:48:19 UTC
(spark) branch branch-4.3 updated: [SPARK-58947][CORE] Handle Char in createArray and add tests for SparkCollectionUtils
This is an automated email from the ASF dual-hosted git repository.
uros-b pushed a commit to branch branch-4.3
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/branch-4.3 by this push:
new 4efb4787564e [SPARK-58947][CORE] Handle Char in createArray and add tests for SparkCollectionUtils
4efb4787564e is described below
commit 4efb4787564e6941a6c2a76179c5bfd4d4f5d1c2
Author: Uros <22...@users.noreply.github.com>
AuthorDate: Tue Aug 25 15:47:49 2026 +0000
[SPARK-58947][CORE] Handle Char in createArray and add tests for SparkCollectionUtils
### What changes were proposed in this pull request?
Adds a unit test suite for `SparkCollectionUtils` (`common/utils`, `org.apache.spark.util`), which had no test coverage, and fixes a gap the tests surfaced.
Tests cover all four methods: `toMapWithIndex` (empty input, duplicate keys, equivalence to `zipWithIndex.toMap`); `isEmpty` / `isNotEmpty` (null, empty, non-empty `java.util.Map`); and `createArray` for every primitive type plus reference types and `size == 0`.
Adding the `createArray` coverage exposed a latent bug: it dispatched on 7 of the 8 primitive `classOf` types (Boolean/Byte/Short/Int/Long/Float/Double) but not `Char`, so a `Char` argument fell through to the `Array[AnyRef]` cast and threw `ClassCastException` (a `char[]` is not an `Object[]`). This adds the missing `Char` case so `createArray[Char]` fills the array like the other primitives.
### Why are the changes needed?
`SparkCollectionUtils` is used in production (e.g. `StructType` field indexing via `toMapWithIndex`) but had no tests. The `Char` gap is currently unreachable (no caller passes `Char`), but it is a latent crash for any future `Char` caller; adding the case makes the dispatch complete and symmetric with the other primitives.
### Does this PR introduce _any_ user-facing change?
No. `SparkCollectionUtils` is `private[spark]`; this adds tests and a missing internal `Char` case.
### How was this patch tested?
New `SparkCollectionUtilsSuite` (extends `AnyFunSuite`, the base used by the sibling `common/utils` suites), covering all four methods including `createArray` for all eight primitive types, reference types, and `size == 0`.
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 4.8)
Closes #58227 from uros-b/test-sparkcollectionutils.
Authored-by: Uros <22...@users.noreply.github.com>
Signed-off-by: Uros Bojanic <22...@users.noreply.github.com>
(cherry picked from commit 0fb568c220dc1e1f797757a9392024e5244f346f)
Signed-off-by: Uros Bojanic <22...@users.noreply.github.com>
---
.../apache/spark/util/SparkCollectionUtils.scala | 2 +
.../spark/util/SparkCollectionUtilsSuite.scala | 64 ++++++++++++++++++++++
2 files changed, 66 insertions(+)
diff --git a/common/utils/src/main/scala/org/apache/spark/util/SparkCollectionUtils.scala b/common/utils/src/main/scala/org/apache/spark/util/SparkCollectionUtils.scala
index 9c255daa0522..956d9c8bc9e3 100644
--- a/common/utils/src/main/scala/org/apache/spark/util/SparkCollectionUtils.scala
+++ b/common/utils/src/main/scala/org/apache/spark/util/SparkCollectionUtils.scala
@@ -52,6 +52,8 @@ private[spark] trait SparkCollectionUtils {
Arrays.fill(arr.asInstanceOf[Array[Byte]], defaultValue.asInstanceOf[Byte])
case c if c == classOf[Short] =>
Arrays.fill(arr.asInstanceOf[Array[Short]], defaultValue.asInstanceOf[Short])
+ case c if c == classOf[Char] =>
+ Arrays.fill(arr.asInstanceOf[Array[Char]], defaultValue.asInstanceOf[Char])
case c if c == classOf[Int] =>
Arrays.fill(arr.asInstanceOf[Array[Int]], defaultValue.asInstanceOf[Int])
case c if c == classOf[Long] =>
diff --git a/common/utils/src/test/scala/org/apache/spark/util/SparkCollectionUtilsSuite.scala b/common/utils/src/test/scala/org/apache/spark/util/SparkCollectionUtilsSuite.scala
new file mode 100644
index 000000000000..8040587e873a
--- /dev/null
+++ b/common/utils/src/test/scala/org/apache/spark/util/SparkCollectionUtilsSuite.scala
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.spark.util
+
+import org.scalatest.funsuite.AnyFunSuite // scalastyle:ignore funsuite
+
+class SparkCollectionUtilsSuite extends AnyFunSuite { // scalastyle:ignore funsuite
+
+ test("toMapWithIndex maps each key to its position, matching zipWithIndex.toMap") {
+ assert(SparkCollectionUtils.toMapWithIndex(Seq("a", "b", "c")) ===
+ Map("a" -> 0, "b" -> 1, "c" -> 2))
+ assert(SparkCollectionUtils.toMapWithIndex(Seq.empty[String]) === Map.empty[String, Int])
+ // Duplicate keys keep the last index, matching zipWithIndex.toMap.
+ assert(SparkCollectionUtils.toMapWithIndex(Seq("a", "a")) === Map("a" -> 1))
+ val keys = Seq(10, 20, 30, 40)
+ assert(SparkCollectionUtils.toMapWithIndex(keys) === keys.zipWithIndex.toMap)
+ }
+
+ test("isEmpty and isNotEmpty handle null, empty and non-empty maps") {
+ val nullMap: java.util.Map[String, Int] = null
+ assert(SparkCollectionUtils.isEmpty(nullMap))
+ assert(!SparkCollectionUtils.isNotEmpty(nullMap))
+
+ val empty = new java.util.HashMap[String, Int]()
+ assert(SparkCollectionUtils.isEmpty(empty))
+ assert(!SparkCollectionUtils.isNotEmpty(empty))
+
+ val nonEmpty = new java.util.HashMap[String, Int]()
+ nonEmpty.put("a", 1)
+ assert(!SparkCollectionUtils.isEmpty(nonEmpty))
+ assert(SparkCollectionUtils.isNotEmpty(nonEmpty))
+ }
+
+ test("createArray fills primitive-typed arrays with the default value") {
+ assert(SparkCollectionUtils.createArray(3, 7) === Array(7, 7, 7))
+ assert(SparkCollectionUtils.createArray(2, true) === Array(true, true))
+ assert(SparkCollectionUtils.createArray(2, 1L) === Array(1L, 1L))
+ assert(SparkCollectionUtils.createArray(2, 2.5d) === Array(2.5d, 2.5d))
+ assert(SparkCollectionUtils.createArray(2, 1.5f) === Array(1.5f, 1.5f))
+ assert(SparkCollectionUtils.createArray(2, 3.toByte) === Array(3.toByte, 3.toByte))
+ assert(SparkCollectionUtils.createArray(2, 4.toShort) === Array(4.toShort, 4.toShort))
+ assert(SparkCollectionUtils.createArray(2, 'a') === Array('a', 'a'))
+ assert(SparkCollectionUtils.createArray(0, 7).isEmpty)
+ }
+
+ test("createArray fills reference-typed arrays and returns empty for size 0") {
+ assert(SparkCollectionUtils.createArray(2, "x") === Array("x", "x"))
+ assert(SparkCollectionUtils.createArray(0, "x").isEmpty)
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@spark.apache.org
For additional commands, e-mail: commits-help@spark.apache.org