Scala Arrays
Arrays are fixed-size data structures that store elements of the same type. In Scala, arrays correspond to Java arrays and are mutable but have fixed size.
Array Basics
Array Creation
scala
object ArrayCreation {
def main(args: Array[String]): Unit = {
// Different ways to create arrays
val arr1 = Array(1, 2, 3, 4, 5)
val arr2 = Array.apply(1, 2, 3, 4, 5)
val arr3 = new Array[Int](5) // Create array of length 5 with default value 0
val arr4 = Array.fill(5)(0) // Fill array of length 5 with 0
val arr5 = Array.tabulate(5)(i => i * i) // Use function to generate array
println(s"arr1: ${arr1.mkString(", ")}")
println(s"arr2: ${arr2.mkString(", ")}")
println(s"arr3: ${arr3.mkString(", ")}")
println(s"arr4: ${arr4.mkString(", ")}")
println(s"arr5: ${arr5.mkString(", ")}")
// Different type arrays
val stringArray = Array("hello", "world", "scala")
val doubleArray = Array(1.1, 2.2, 3.3)
val booleanArray = Array(true, false, true)
println(s"String array: ${stringArray.mkString(", ")}")
println(s"Double array: ${doubleArray.mkString(", ")}")
println(s"Boolean array: ${booleanArray.mkString(", ")}")
// Multi-dimensional arrays
val matrix = Array.ofDim[Int](3, 4) // 3x4 two-dimensional array
val cube = Array.ofDim[Int](2, 3, 4) // 2x3x4 three-dimensional array
println(s"Matrix dimensions: ${matrix.length} x ${matrix(0).length}")
println(s"Cube dimensions: ${cube.length} x ${cube(0).length} x ${cube(0)(0).length}")
}
}Array Access and Modification
scala
object ArrayAccess {
def main(args: Array[String]): Unit = {
val numbers = Array(10, 20, 30, 40, 50)
// Access elements
println(s"First element: ${numbers(0)}")
println(s"Last element: ${numbers(numbers.length - 1)}")
println(s"Array length: ${numbers.length}")
// Modify elements
numbers(0) = 100
numbers(numbers.length - 1) = 500
println(s"Modified array: ${numbers.mkString(", ")}")
// Safe access (avoid index out of bounds)
def safeGet(arr: Array[Int], index: Int): Option[Int] = {
if (index >= 0 && index < arr.length) Some(arr(index))
else None
}
println(s"Safe get index 2: ${safeGet(numbers, 2)}")
println(s"Safe get index 10: ${safeGet(numbers, 10)}")
// Use lift method
val lifted = numbers.lift
println(s"Lift index 2: ${lifted(2)}")
println(s"Lift index 10: ${lifted(10)}")
}
}Array Operations
Basic Operations
scala
object BasicArrayOperations {
def main(args: Array[String]): Unit = {
val arr = Array(1, 2, 3, 4, 5)
// Basic properties
println(s"Length: ${arr.length}")
println(s"Is empty: ${arr.isEmpty}")
println(s"Non empty: ${arr.nonEmpty}")
// Search operations
println(s"Contains 3: ${arr.contains(3)}")
println(s"Index of 3: ${arr.indexOf(3)}")
println(s"Last index of 3: ${arr.lastIndexOf(3)}")
// Head and tail operations
println(s"Head: ${arr.head}")
println(s"Tail: ${arr.tail.mkString(", ")}")
println(s"Last: ${arr.last}")
println(s"Init: ${arr.init.mkString(", ")}")
// Slice operations
println(s"Slice(1, 4): ${arr.slice(1, 4).mkString(", ")}")
println(s"Take(3): ${arr.take(3).mkString(", ")}")
println(s"Drop(2): ${arr.drop(2).mkString(", ")}")
println(s"Take right(2): ${arr.takeRight(2).mkString(", ")}")
println(s"Drop right(2): ${arr.dropRight(2).mkString(", ")}")
// Reverse
println(s"Reverse: ${arr.reverse.mkString(", ")}")
// Sorting
val unsorted = Array(5, 2, 8, 1, 9)
println(s"Sorted: ${unsorted.sorted.mkString(", ")}")
println(s"Sorted descending: ${unsorted.sortWith(_ > _).mkString(", ")}")
}
}Functional Operations
scala
object FunctionalArrayOperations {
def main(args: Array[String]): Unit = {
val numbers = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// map - transform each element
val doubled = numbers.map(_ * 2)
println(s"Doubled: ${doubled.mkString(", ")}")
// filter - filter elements
val evens = numbers.filter(_ % 2 == 0)
println(s"Even numbers: ${evens.mkString(", ")}")
// find - find first matching element
val firstEven = numbers.find(_ % 2 == 0)
println(s"First even: $firstEven")
// exists - check if any element satisfies condition
val hasEven = numbers.exists(_ % 2 == 0)
println(s"Has even numbers: $hasEven")
// forall - check if all elements satisfy condition
val allPositive = numbers.forall(_ > 0)
println(s"All positive: $allPositive")
// reduce - reduction operation
val sum = numbers.reduce(_ + _)
val max = numbers.reduce(_ max _)
println(s"Sum: $sum")
println(s"Max: $max")
// fold - reduction with initial value
val product = numbers.fold(1)(_ * _)
val sumWithZero = numbers.fold(0)(_ + _)
println(s"Product: $product")
println(s"Sum with zero: $sumWithZero")
// scan - scan operation (keeps intermediate results)
val runningSum = numbers.scanLeft(0)(_ + _)
println(s"Running sum: ${runningSum.mkString(", ")}")
// partition - split
val (evens2, odds) = numbers.partition(_ % 2 == 0)
println(s"Evens: ${evens2.mkString(", ")}")
println(s"Odds: ${odds.mkString(", ")}")
// groupBy - grouping
val grouped = numbers.groupBy(_ % 3)
grouped.foreach { case (remainder, group) =>
println(s"Remainder $remainder: ${group.mkString(", ")}")
}
}
}Array Concatenation and Conversion
scala
object ArrayConcatenationConversion {
def main(args: Array[String]): Unit = {
val arr1 = Array(1, 2, 3)
val arr2 = Array(4, 5, 6)
// Concatenate arrays
val concatenated = arr1 ++ arr2
println(s"Concatenated: ${concatenated.mkString(", ")}")
// Use concat
val concatenated2 = Array.concat(arr1, arr2)
println(s"Concatenated2: ${concatenated2.mkString(", ")}")
// Add elements
val withPrepended = 0 +: arr1
val withAppended = arr1 :+ 4
println(s"Prepended: ${withPrepended.mkString(", ")}")
println(s"Appended: ${withAppended.mkString(", ")}")
// Convert to other collection types
val list = arr1.toList
val vector = arr1.toVector
val set = arr1.toSet
println(s"To List: $list")
println(s"To Vector: $vector")
println(s"To Set: $set")
// Convert from other collection types
val fromList = List(7, 8, 9).toArray
val fromRange = (1 to 5).toArray
println(s"From List: ${fromList.mkString(", ")}")
println(s"From Range: ${fromRange.mkString(", ")}")
// String conversion
val chars = "hello".toCharArray
val string = chars.mkString
println(s"String to chars: ${chars.mkString(", ")}")
println(s"Chars to string: $string")
}
}Multi-Dimensional Arrays
Two-Dimensional Array Operations
scala
object TwoDimensionalArrays {
def main(args: Array[String]): Unit = {
// Create two-dimensional array
val matrix = Array(
Array(1, 2, 3),
Array(4, 5, 6),
Array(7, 8, 9)
)
// Access elements
println(s"Element at (1,1): ${matrix(1)(1)}")
// Print matrix
def printMatrix(matrix: Array[Array[Int]]): Unit = {
matrix.foreach(row => println(row.mkString("\t")))
}
println("Original matrix:")
printMatrix(matrix)
// Matrix transpose
def transpose(matrix: Array[Array[Int]]): Array[Array[Int]] = {
val rows = matrix.length
val cols = matrix(0).length
val transposed = Array.ofDim[Int](cols, rows)
for (i <- matrix.indices; j <- matrix(i).indices) {
transposed(j)(i) = matrix(i)(j)
}
transposed
}
println("\nTransposed matrix:")
printMatrix(transpose(matrix))
// Matrix addition
def addMatrices(a: Array[Array[Int]], b: Array[Array[Int]]): Array[Array[Int]] = {
require(a.length == b.length && a(0).length == b(0).length, "Matrices must have same dimensions")
val result = Array.ofDim[Int](a.length, a(0).length)
for (i <- a.indices; j <- a(i).indices) {
result(i)(j) = a(i)(j) + b(i)(j)
}
result
}
val matrix2 = Array(
Array(9, 8, 7),
Array(6, 5, 4),
Array(3, 2, 1)
)
println("\nMatrix addition:")
printMatrix(addMatrices(matrix, matrix2))
// Find maximum value and position
def findMax(matrix: Array[Array[Int]]): (Int, Int, Int) = {
var maxValue = matrix(0)(0)
var maxRow = 0
var maxCol = 0
for (i <- matrix.indices; j <- matrix(i).indices) {
if (matrix(i)(j) > maxValue) {
maxValue = matrix(i)(j)
maxRow = i
maxCol = j
}
}
(maxValue, maxRow, maxCol)
}
val (maxVal, maxRow, maxCol) = findMax(matrix)
println(s"\nMax value: $maxVal at position ($maxRow, $maxCol)")
}
}Array Performance and Memory
Performance Comparison
scala
object ArrayPerformance {
def main(args: Array[String]): Unit = {
val size = 1000000
// Array vs List performance comparison
def timeOperation[T](operation: => T): (T, Long) = {
val start = System.nanoTime()
val result = operation
val end = System.nanoTime()
(result, end - start)
}
// Creation performance
val (array, arrayTime) = timeOperation(Array.fill(size)(scala.util.Random.nextInt(100)))
val (list, listTime) = timeOperation(List.fill(size)(scala.util.Random.nextInt(100)))
println(s"Array creation: ${arrayTime / 1000000}ms")
println(s"List creation: ${listTime / 1000000}ms")
// Random access performance
val index = size / 2
val (arrayAccess, arrayAccessTime) = timeOperation(array(index))
val (listAccess, listAccessTime) = timeOperation(list(index))
println(s"Array random access: ${arrayAccessTime}ns")
println(s"List random access: ${listAccessTime}ns")
// Traversal performance
val (arraySum, arraySumTime) = timeOperation(array.sum)
val (listSum, listSumTime) = timeOperation(list.sum))
println(s"Array sum: ${arraySumTime / 1000000}ms")
println(s"List sum: ${listSumTime / 1000000}ms")
// Memory usage
println(s"\nMemory considerations:")
println(s"Array: Fixed size, mutable elements, efficient random access")
println(s"List: Immutable, efficient prepend, poor random access")
}
}Array Buffers
scala
import scala.collection.mutable.ArrayBuffer
object ArrayBufferExample {
def main(args: Array[String]): Unit = {
// ArrayBuffer - mutable size array
val buffer = ArrayBuffer[Int]()
// Add elements
buffer += 1
buffer += 2
buffer ++= Array(3, 4, 5)
println(s"Buffer: ${buffer.mkString(", ")}")
// Insert and delete
buffer.insert(2, 99) // Insert 99 at index 2
buffer.remove(0) // Delete element at index 0
println(s"After insert/remove: ${buffer.mkString(", ")}")
// Convert to array
val finalArray = buffer.toArray
println(s"Final array: ${finalArray.mkString(", ")}")
// Performance comparison: Array vs ArrayBuffer
def buildWithArray(n: Int): Array[Int] = {
var arr = Array[Int]()
for (i <- 1 to n) {
arr = arr :+ i // Creates new array each time, inefficient
}
arr
}
def buildWithArrayBuffer(n: Int): Array[Int] = {
val buffer = ArrayBuffer[Int]()
for (i <- 1 to n) {
buffer += i // Efficient append operation
}
buffer.toArray
}
val n = 10000
val (_, arrayTime) = {
val start = System.nanoTime()
buildWithArray(n)
val end = System.nanoTime()
((), end - start)
}
val (_, bufferTime) = {
val start = System.nanoTime()
buildWithArrayBuffer(n)
val end = System.nanoTime()
((), end - start)
}
println(s"\nBuilding array with Array: ${arrayTime / 1000000}ms")
println(s"Building array with ArrayBuffer: ${bufferTime / 1000000}ms")
}
}Practical Application Examples
Image Processing
scala
object ImageProcessing {
// Simplified image representation (grayscale)
type Image = Array[Array[Int]]
def createImage(width: Int, height: Int, value: Int = 0): Image = {
Array.fill(height, width)(value)
}
def printImage(image: Image): Unit = {
image.foreach(row => println(row.map(v => f"$v%3d").mkString(" ")))
}
// Apply filter
def applyFilter(image: Image, filter: Array[Array[Double]]): Image = {
val height = image.length
val width = image(0).length
val filterSize = filter.length
val offset = filterSize / 2
val result = Array.ofDim[Int](height, width)
for (i <- offset until height - offset; j <- offset until width - offset) {
var sum = 0.0
for (fi <- filter.indices; fj <- filter(fi).indices) {
sum += image(i - offset + fi)(j - offset + fj) * filter(fi)(fj)
}
result(i)(j) = math.max(0, math.min(255, sum.toInt))
}
result
}
// Edge detection filter
val edgeFilter = Array(
Array(-1.0, -1.0, -1.0),
Array(-1.0, 8.0, -1.0),
Array(-1.0, -1.0, -1.0)
)
// Blur filter
val blurFilter = Array(
Array(1.0/9, 1.0/9, 1.0/9),
Array(1.0/9, 1.0/9, 1.0/9),
Array(1.0/9, 1.0/9, 1.0/9)
)
def main(args: Array[String]): Unit = {
// Create test image
val image = Array(
Array(100, 100, 100, 100, 100),
Array(100, 200, 200, 200, 100),
Array(100, 200, 255, 200, 100),
Array(100, 200, 200, 200, 100),
Array(100, 100, 100, 100, 100)
)
println("Original image:")
printImage(image)
println("\nAfter edge detection:")
val edges = applyFilter(image, edgeFilter)
printImage(edges)
println("\nAfter blur:")
val blurred = applyFilter(image, blurFilter)
printImage(blurred)
}
}Data Analysis
scala
object DataAnalysis {
case class Student(name: String, scores: Array[Double]) {
def average: Double = scores.sum / scores.length
def max: Double = scores.max
def min: Double = scores.min
}
def analyzeScores(students: Array[Student]): Unit = {
println("Student Analysis:")
println("=" * 50)
students.foreach { student =>
println(f"${student.name}%15s: Avg=${student.average}%6.2f, Max=${student.max}%6.2f, Min=${student.min}%6.2f")
}
// Class statistics
val allScores = students.flatMap(_.scores)
val classAverage = allScores.sum / allScores.length
val classMax = allScores.max
val classMin = allScores.min
println("\nClass Statistics:")
println(f"Average: $classAverage%.2f")
println(f"Highest: $classMax%.2f")
println(f"Lowest: $classMin%.2f")
// Grade distribution
val ranges = Array(
("A (90-100)", (90.0, 100.0)),
("B (80-89)", (80.0, 89.9)),
("C (70-79)", (70.0, 79.9)),
("D (60-69)", (60.0, 69.9)),
("F (0-59)", (0.0, 59.9))
)
println("\nGrade Distribution:")
ranges.foreach { case (grade, (min, max)) =>
val count = allScores.count(score => score >= min && score <= max)
val percentage = count.toDouble / allScores.length * 100
println(f"$grade%12s: $count%3d students ($percentage%5.1f%%)")
}
}
def main(args: Array[String]): Unit = {
val students = Array(
Student("Alice", Array(95, 87, 92, 88, 91)),
Student("Bob", Array(78, 82, 85, 79, 83)),
Student("Charlie", Array(92, 95, 89, 94, 96)),
Student("Diana", Array(67, 72, 69, 74, 71)),
Student("Eve", Array(88, 91, 85, 89, 87))
)
analyzeScores(students)
}
}Best Practices
Choose the right data structure:
- Fixed size and need random access: Use
Array - Dynamic size: Use
ArrayBuffer - Immutable and functional operations: Use
ListorVector
- Fixed size and need random access: Use
Performance considerations:
- Arrays provide O(1) random access
- Avoid frequent resize operations
- Use
ArrayBufferfor dynamic building
Memory efficiency:
- Arrays store elements contiguously in memory
- Avoid unnecessary array copying
- Consider using primitive type arrays (like
Array[Int])
Functional programming:
- Prioritize
map,filter,reduceand other higher-order functions - Avoid mutable state and side effects
- Use immutable collection types
- Prioritize
Error handling:
- Use
liftmethod for safe access - Check array bounds
- Use
Optiontype to handle possible null values
- Use
Arrays are important data structures in Scala, understanding their characteristics and optimal usage is essential for writing efficient programs.