大数据技术实践者头像
Scala Array操作:从定长数组到ArrayBuffer的完整指南封面图

Scala Array操作:从定长数组到ArrayBuffer的完整指南

Scala的Array就是Java数组的封装,但Scala给它装上了集合操作的"超能力"。索引访问、map/filter、与List互转——让数组处理从Java的for循环中解放出来。

如果你从Java转到Scala,第一次用数组时可能会有疑问:Scala的Array和Java的数组是同一个东西吗?为什么可以直接用map和filter?

第一个问题的答案是:是的,Scala的Array编译后就是Java的数组(int[]、String[]),JVM层面完全一样。第二个问题的答案是:Scala通过隐式转换,把Array当作Seq来操作——你写的是数组,用的却是集合的API。

在大数据开发中,数组无处不在:Spark RDD的分区数据是数组、Flink的窗口缓冲区是数组、NumPy风格的数值计算依赖数组。理解Scala的Array,就是理解大数据内存计算的底层数据结构。

这篇文章从七个层面拆解Scala的Array操作,帮你从Java的数组处理中平滑迁移。

Scala Array操作知识体系


01 Array基础:定长、可变、与Java互操作

Array就是Java数组

// Scala的Array编译后就是Java数组
val arr = Array(1, 2, 3, 4, 5)
// 编译后等价于Java的 int[] arr = {1,2,3,4,5};

// 可以直接索引访问
arr(0)              // 1
arr(4)              // 5

// 可以修改元素(数组是可变的)
arr(0) = 10
// arr: Array(10, 2, 3, 4, 5)

Array是定长的

val arr = new Array[Int](5)
// 长度固定为5,不能动态扩展

arr(0) = 1
arr(1) = 2
// arr: Array(1, 2, 0, 0, 0)

// arr += 6  // 编译错误:Array长度固定

Array与Java数组互操作

// Scala Array就是Java数组,可以直接传给Java方法
def processJavaArray(arr: Array[Int]): Unit = {
  // 这个arr在JVM层面就是int[]
  arr.foreach(println)
}

// 也可以从Java数组创建Scala Array
val javaArr: Array[Int] = Array(1, 2, 3)
// 完全等价

对Java开发者的启示:Java数组和Scala数组在JVM层面是同一个东西,互操作零成本。这意味着你可以在Scala代码中直接调用Java库,传入数组参数,不需要任何转换。


02 Array创建与初始化

用apply方法创建

// 最常用:Array.apply
val arr1 = Array(1, 2, 3, 4, 5)
// Array[Int] = Array(1, 2, 3, 4, 5)

// 混合类型会自动推断公共父类型
val arr2 = Array(1, "two", 3.0)
// Array[Any] = Array(1, two, 3.0)

// 空数组
val empty = Array.empty[String]
// Array[String] = Array()

用new创建指定长度

// 创建指定长度的数组,元素为默认值
val arr1 = new Array[Int](5)
// Array(0, 0, 0, 0, 0)

val arr2 = new Array[String](3)
// Array(null, null, null)

val arr3 = new Array[Boolean](2)
// Array(false, false)

常用初始化方法

// range:生成范围数组
Array.range(1, 10)         // Array(1,2,3,4,5,6,7,8,9)
Array.range(1, 10, 2)       // Array(1,3,5,7,9),步长2

// fill:填充相同值
Array.fill(5)("hello")      // Array(hello, hello, hello, hello, hello)
Array.fill(3, 2)(0)         // 二维数组 Array(Array(0,0), Array(0,0), Array(0,0))

// tabulate:按函数生成
Array.tabulate(5)(n => n * n)
// Array(0, 1, 4, 9, 16)

// concatenate:合并数组
Array.concat(Array(1,2), Array(3,4))
// Array(1, 2, 3, 4)

03 Array常用操作

索引访问与修改

val arr = Array(10, 20, 30, 40, 50)

// 读取
arr(0)                    // 10
arr.last                  // 50
arr.head                  // 10
arr.apply(2)              // 30,等价于arr(2)

// 修改
arr(0) = 100
// arr: Array(100, 20, 30, 40, 50)

// 长度
arr.length                // 5
arr.size                  // 5,等价于length

遍历

val arr = Array("a", "b", "c")

// 最简单:foreach
arr.foreach(println)

// 带索引
for ((elem, i) <- arr.zipWithIndex) {
  println(s"$i: $elem")
}

// 传统for循环
for (i <- 0 until arr.length) {
  println(arr(i))
}

转换与映射

val arr = Array(1, 2, 3, 4, 5)

// map:每个元素转换
arr.map(_ * 2)
// Array(2, 4, 6, 8, 10)

// filter:过滤元素
arr.filter(_ % 2 == 0)
// Array(2, 4)

// reduce:聚合
arr.reduce(_ + _)
// 15

// fold:带初始值的聚合
arr.fold(0)(_ + _)
// 15

排序

val arr = Array(3, 1, 4, 1, 5, 9, 2, 6)

// 升序排序(返回新数组)
arr.sorted
// Array(1, 1, 2, 3, 4, 5, 6, 9)

// 降序
arr.sortWith(_ > _)
// Array(9, 6, 5, 4, 3, 2, 1, 1)

// 按指定字段排序
case class Person(name: String, age: Int)
val people = Array(Person("Alice", 25), Person("Bob", 20), Person("Charlie", 30))
people.sortBy(_.age)
// 按年龄升序

04 ArrayBuffer:可变数组

Array是定长的,需要动态扩展时用ArrayBuffer。

创建与操作

import scala.collection.mutable.ArrayBuffer

// 创建ArrayBuffer
val buf = ArrayBuffer[Int]()
// ArrayBuffer()

// 添加元素
buf += 1
buf += 2
buf += 3
// ArrayBuffer(1, 2, 3)

// 添加多个元素
buf += (4, 5, 6)
// ArrayBuffer(1, 2, 3, 4, 5, 6)

// 追加另一个集合
buf ++= Array(7, 8)
// ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8)

删除与修改

val buf = ArrayBuffer(1, 2, 3, 4, 5)

// 删除最后一个元素
buf.remove(buf.length - 1)
// ArrayBuffer(1, 2, 3, 4)

// 删除指定位置
buf.remove(0)
// ArrayBuffer(2, 3, 4)

// 修改
buf(0) = 100
// ArrayBuffer(100, 3, 4)

转换为Array

val buf = ArrayBuffer(1, 2, 3, 4, 5)

// 转换为定长Array
val arr = buf.toArray
// Array(1, 2, 3, 4, 5)

// 转换为List
val list = buf.toList
// List(1, 2, 3, 4, 5)

05 Array与集合互转

Array ↔ List

val arr = Array(1, 2, 3, 4, 5)

// Array转List
val list = arr.toList
// List(1, 2, 3, 4, 5)

// List转Array
val arr2 = list.toArray
// Array(1, 2, 3, 4, 5)

// Array转Seq
val seq = arr.toSeq
// WrappedArray(1, 2, 3, 4, 5)

Array与集合操作的关系

val arr = Array(1, 2, 3, 4, 5)

// Array本身不是Seq,但可以通过隐式转换使用Seq的方法
arr.map(_ * 2)           // 返回Array
arr.filter(_ > 2)        // 返回Array
arr.reduce(_ + _)        // 返回Int
arr.mkString(", ")       // "1, 2, 3, 4, 5"

// 注意:map/filter返回的还是Array
arr.map(_ * 2).getClass.getSimpleName
// int[](基本类型数组)或 Object[]

WrappedArray:Array的Seq视图

val arr = Array(1, 2, 3)

// 当你需要Seq类型时,Array会自动转换为WrappedArray
val seq: Seq[Int] = arr
// WrappedArray(1, 2, 3)

// WrappedArray是Array的Seq视图,不复制数据
// 修改原数组,视图也会变
arr(0) = 100
println(seq)
// WrappedArray(100, 2, 3)

大数据场景提示:Spark的RDD.collect()返回的就是Array,而RDD的map/filter返回的是RDD。理解Array和集合操作的关系,有助于理解Spark转换操作的底层实现。


06 多维数组

创建多维数组

// 二维数组:3行4列
val matrix = Array.ofDim[Int](3, 4)
// Array(Array(0,0,0,0), Array(0,0,0,0), Array(0,0,0,0))

// 赋值
matrix(0)(0) = 1
matrix(1)(1) = 2
// Array(Array(1,0,0,0), Array(0,2,0,0), Array(0,0,0,0))

// 创建并初始化
val grid = Array(
  Array(1, 2, 3),
  Array(4, 5, 6),
  Array(7, 8, 9)
)
// 3x3矩阵

多维数组操作

val matrix = Array(
  Array(1, 2, 3),
  Array(4, 5, 6),
  Array(7, 8, 9)
)

// 访问
matrix(0)(0)             // 1
matrix(1)(2)             // 6

// 遍历所有元素
for {
  row <- matrix
  elem <- row
} print(elem + " ")
// 1 2 3 4 5 6 7 8 9

// 扁平化:二维数组转一维
matrix.flatten
// Array(1, 2, 3, 4, 5, 6, 7, 8, 9)

07 与Java数组的对比

理解Scala和Java数组操作的差异,能帮助你避免常见的迁移陷阱。

Java数组操作vs Scala数组操作

核心差异对比

维度JavaScala
数组创建int[] arr = new int[5]Array(1,2,3) 或 new ArrayInt
索引访问arr[0]arr(0)
长度arr.lengtharr.length 或 arr.size
遍历for(int i=0;…)arr.foreach 或 for推导式
转换Arrays.stream(arr)arr.map/filter/reduce
排序Arrays.sort(arr)arr.sorted 或 arr.sortBy
拼接Arrays.toString(arr)arr.mkString(", ")
可变数组ArrayListArrayBuffer
互操作本身就是数组编译后就是Java数组

Java数组操作 vs Scala数组操作

// Java: 数组创建
int[] arr = new int[5];
arr[0] = 1;
arr[1] = 2;

// Java: 数组遍历
for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}

// Java: 数组排序
Arrays.sort(arr);

// Java: 数组转字符串
System.out.println(Arrays.toString(arr));
// Scala: 数组创建
val arr = Array(1, 2, 3, 4, 5)

// Scala: 数组遍历
arr.foreach(println)

// Scala: 数组排序
arr.sorted

// Scala: 数组转字符串
arr.mkString(", ")

Scala数组工具方法

// 常用工具方法
import scala.util.Sorting.quickSort

val arr = Array(3, 1, 4, 1, 5, 9, 2, 6)

// 快速排序(原地修改)
quickSort(arr)
// arr: Array(1, 1, 2, 3, 4, 5, 6, 9)

// 数组合并
Array.concat(Array(1,2), Array(3,4))

// 数组填充
Array.fill(5)(0)

写在最后:数组是数据工程的底层数据结构

Scala的Array,表面上是Java数组的"语法糖封装",本质上是把数组从"一个固定大小的内存块"提升为"一个可以函数式操作的集合"。

这种提升带来三个好处:

  1. 更简洁的操作:map/filter/reduce替代了Java的for循环和Stream,代码更易读
  2. 更强的互操作性:Scala Array编译后就是Java数组,与Java库零成本互操作
  3. 更灵活的扩展:ArrayBuffer提供了动态扩展能力,不需要手动管理数组大小

这些特性不是孤立的,它们共同支撑了Scala的数据处理范式。Spark的RDD分区数据是数组、Flink的窗口缓冲区是数组、NumPy风格的数值计算依赖数组——这些框架的底层都是Scala数组操作的分布式版本。

学习建议:不要试图一次记住所有方法。先从数组创建和索引访问开始,写一些小练习。当你习惯了用map/filter处理数组后,再学习ArrayBuffer和多维数组。数组是编程的基本功,但在Scala里,它不仅是基本功——它是函数式数据处理的入口。

从Java的数组遍历和操作,到Scala的map/filter/reduce,不是学习"另一种数组API",而是学习一种"更函数式、更声明式、更组合的数据处理方式"。当你习惯了用Array.apply创建、用foreach遍历、用map转换、用reduce聚合,你会发现:原来数组处理可以这么优雅。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/zmj_0817/article/details/167225052

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--