Skip to content

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

  1. Choose the right data structure:

    • Fixed size and need random access: Use Array
    • Dynamic size: Use ArrayBuffer
    • Immutable and functional operations: Use List or Vector
  2. Performance considerations:

    • Arrays provide O(1) random access
    • Avoid frequent resize operations
    • Use ArrayBuffer for dynamic building
  3. Memory efficiency:

    • Arrays store elements contiguously in memory
    • Avoid unnecessary array copying
    • Consider using primitive type arrays (like Array[Int])
  4. Functional programming:

    • Prioritize map, filter, reduce and other higher-order functions
    • Avoid mutable state and side effects
    • Use immutable collection types
  5. Error handling:

    • Use lift method for safe access
    • Check array bounds
    • Use Option type to handle possible null values

Arrays are important data structures in Scala, understanding their characteristics and optimal usage is essential for writing efficient programs.

Content is for learning and research only.