Scala - Array with Examples
Scala provides multiple ways to instantiate arrays depending on your use case. The most common approach uses the `Array` companion object's `apply` method.
Key Insights
- Scala arrays are mutable, fixed-size collections that map directly to Java arrays, offering O(1) element access and maximum performance for numerical computations
- Array operations in Scala provide both imperative mutation methods and functional transformation methods that return new arrays, giving you flexibility in programming style
- Multi-dimensional arrays, array buffers for dynamic sizing, and seamless Java interoperability make Scala arrays practical for real-world applications from data processing to performance-critical systems
Creating Arrays
Scala provides multiple ways to instantiate arrays depending on your use case. The most common approach uses the Array companion object’s apply method.
// Direct initialization with elements
val numbers = Array(1, 2, 3, 4, 5)
val strings = Array("Scala", "Java", "Kotlin")
// Creating arrays with specific size and default values
val zeros = new Array[Int](10) // Array of 10 zeros
val emptyStrings = new Array[String](5) // Array of 5 nulls
// Using Array.fill for custom initialization
val ones = Array.fill(5)(1) // Array(1, 1, 1, 1, 1)
val random = Array.fill(5)(scala.util.Random.nextInt(100))
// Using Array.tabulate for index-based initialization
val squares = Array.tabulate(5)(n => n * n) // Array(0, 1, 4, 9, 16)
// Range conversion to array
val range = (1 to 10).toArray
The type parameter is inferred from the elements, but you can specify it explicitly when needed:
val mixed: Array[Any] = Array(1, "two", 3.0, true)
val numbers: Array[Number] = Array(1, 2.5, 3L)
Accessing and Modifying Elements
Arrays use zero-based indexing with parentheses for both reading and writing. This syntax differs from most collections, which use parentheses only for reading.
val arr = Array(10, 20, 30, 40, 50)
// Reading elements
val first = arr(0) // 10
val last = arr(arr.length - 1) // 50
// Modifying elements (arrays are mutable)
arr(0) = 100
arr(2) = arr(1) + arr(3) // 120
// Safe access with bounds checking
val safeGet = arr.lift(10) // Option[Int] = None
val withDefault = arr.applyOrElse(10, (_: Int) => -1) // -1
// Pattern matching on array size
arr match {
case Array(x) => println(s"Single element: $x")
case Array(x, y) => println(s"Two elements: $x, $y")
case Array(x, y, _*) => println(s"At least two: $x, $y")
case _ => println("Empty array")
}
Array Operations and Transformations
Scala arrays support both imperative operations that modify in place and functional operations that return new arrays.
val numbers = Array(1, 2, 3, 4, 5)
// Functional transformations (return new arrays)
val doubled = numbers.map(_ * 2) // Array(2, 4, 6, 8, 10)
val evens = numbers.filter(_ % 2 == 0) // Array(2, 4)
val sum = numbers.reduce(_ + _) // 15
// Chaining operations
val result = numbers
.filter(_ > 2)
.map(_ * 10)
.sorted // Array(30, 40, 50)
// FlatMap for nested structures
val nested = Array(Array(1, 2), Array(3, 4))
val flattened = nested.flatMap(identity) // Array(1, 2, 3, 4)
// Zip and unzip
val letters = Array("a", "b", "c")
val zipped = numbers.zip(letters) // Array((1,a), (2,b), (3,c))
// GroupBy for categorization
val grouped = numbers.groupBy(_ % 2)
// Map(1 -> Array(1, 3, 5), 0 -> Array(2, 4))
For in-place modifications:
val mutable = Array(5, 2, 8, 1, 9)
// Sorting in place
scala.util.Sorting.quickSort(mutable) // mutable is now sorted
// Transform in place
for (i <- mutable.indices) {
mutable(i) = mutable(i) * 2
}
Multi-Dimensional Arrays
Multi-dimensional arrays are arrays of arrays, useful for matrix operations and grid-based data structures.
// Creating 2D arrays
val matrix = Array.ofDim[Int](3, 4) // 3x4 matrix of zeros
// Initializing with values
val grid = Array(
Array(1, 2, 3),
Array(4, 5, 6),
Array(7, 8, 9)
)
// Accessing elements
val element = grid(1)(2) // 6 (row 1, column 2)
grid(0)(0) = 100
// Iterating over 2D array
for {
i <- grid.indices
j <- grid(i).indices
} {
println(s"grid($i)($j) = ${grid(i)(j)}")
}
// Matrix operations
def transposeMatrix(matrix: Array[Array[Int]]): Array[Array[Int]] = {
val rows = matrix.length
val cols = matrix(0).length
Array.tabulate(cols, rows)((i, j) => matrix(j)(i))
}
val transposed = transposeMatrix(grid)
// 3D arrays
val cube = Array.ofDim[Double](10, 10, 10)
cube(5)(5)(5) = 42.0
ArrayBuffer for Dynamic Arrays
When you need a resizable array, use ArrayBuffer from the mutable collections library.
import scala.collection.mutable.ArrayBuffer
val buffer = ArrayBuffer[Int]()
// Adding elements
buffer += 1 // Append single element
buffer += (2, 3, 4) // Append multiple elements
buffer ++= Array(5, 6, 7) // Append collection
buffer.prepend(0) // Add to beginning
buffer.insert(4, 99) // Insert at index
// Removing elements
buffer -= 99 // Remove by value
buffer.remove(0) // Remove by index
buffer.trimEnd(2) // Remove last 2 elements
// Convert to immutable array
val finalArray = buffer.toArray
// Efficient batch operations
val large = ArrayBuffer.fill(10000)(0)
large.transform(_ + 1) // In-place transformation
Performance Considerations and Best Practices
Arrays provide the best performance for indexed access and numerical computations, but understanding their characteristics helps you choose the right collection.
// Benchmark: Array vs List for indexed access
def arrayAccess(arr: Array[Int], n: Int): Long = {
val start = System.nanoTime()
var sum = 0
for (i <- 0 until n) {
sum += arr(i % arr.length)
}
System.nanoTime() - start
}
val testArray = Array.fill(1000)(1)
val iterations = 1000000
val arrayTime = arrayAccess(testArray, iterations)
// Arrays excel at:
// 1. Random access O(1)
// 2. Memory efficiency (contiguous storage)
// 3. Primitive type storage without boxing
// Use ArrayBuffer when:
val dynamic = ArrayBuffer[String]()
// - Size is unknown upfront
// - Frequent additions/removals
// - Eventually convert to Array
// Avoid arrays for:
// - Functional programming patterns requiring immutability
// - Frequent prepend operations (use List)
// - Need for structural sharing (use Vector)
Java Interoperability
Scala arrays compile to Java arrays, enabling seamless integration with Java libraries.
// Scala Array is Java array
val scalaArray: Array[String] = Array("a", "b", "c")
val javaArray: Array[String] = scalaArray // No conversion needed
// Calling Java methods expecting arrays
import java.util.Arrays
val sorted = Arrays.copyOf(scalaArray, scalaArray.length)
Arrays.sort(sorted)
// Converting Java collections to Scala arrays
import scala.jdk.CollectionConverters._
val javaList = java.util.Arrays.asList("x", "y", "z")
val scalaArr = javaList.asScala.toArray
// Varargs compatibility
def javaVarargs(args: String*): Unit = {
println(args.mkString(", "))
}
javaVarargs(scalaArray: _*) // Expand array to varargs
// Working with primitive arrays
val intArray: Array[Int] = Array(1, 2, 3)
// Compiles to Java's int[], not Integer[]
// No boxing overhead for primitives
Advanced Array Patterns
Leverage Scala’s advanced features for sophisticated array manipulation.
// Sliding windows
val data = Array(1, 2, 3, 4, 5)
val windows = data.sliding(3).toArray
// Array(Array(1,2,3), Array(2,3,4), Array(3,4,5))
// Parallel processing for large arrays
val large = Array.fill(1000000)(scala.util.Random.nextInt(100))
val parallelResult = large.par.map(_ * 2).toArray
// Custom ordering
case class Person(name: String, age: Int)
val people = Array(
Person("Alice", 30),
Person("Bob", 25),
Person("Charlie", 35)
)
val byAge = people.sortBy(_.age)
val byName = people.sortWith(_.name < _.name)
// Binary search (requires sorted array)
val sorted = Array(1, 3, 5, 7, 9, 11, 13)
val index = sorted.search(7) // Found(3)
// Deep equality for nested arrays
val arr1 = Array(Array(1, 2), Array(3, 4))
val arr2 = Array(Array(1, 2), Array(3, 4))
val equal = arr1.deep == arr2.deep // true
Arrays remain fundamental in Scala for performance-critical code, numerical computing, and Java interoperability. Choose arrays when you need maximum speed and fixed-size storage, ArrayBuffer for dynamic sizing, and consider immutable alternatives like Vector for functional programming patterns.