Scala的List不是Java的List------它是不可变的链表,是函数式编程的核心数据结构。head/tail递归、模式匹配、::构造,让列表处理从Java的for循环中解放出来。
如果你从Java转到Scala,第一次用List时可能会有两个疑问:Scala的List和Java的List是同一个东西吗?为什么用::而不是add()?
第一个问题的答案是:完全不同。Java的java.util.List是可变的接口,ArrayList是最常用的实现;而Scala的List是不可变的链表 ,继承自Seq,默认是scala.collection.immutable.List。第二个问题的答案是:Scala List用::(cons操作)来构造新列表------这不是语法糖,而是函数式编程的核心操作。
在大数据开发中,List无处不在:Spark RDD的collect返回Array,但转换过程中大量使用List风格的链式调用;Flink的数据流转换、AI管道的数据预处理、ETL中的中间结果收集------都离不开不可变列表的思维。理解Scala的List,就是理解函数式数据处理的核心。
这篇文章从七个层面拆解Scala的List操作,帮你从Java的可变List思维平滑迁移到Scala的不可变List思维。

01 List基础:不可变的链表结构
List是不可变的
scala
// Scala的List是不可变的
val list = List(1, 2, 3, 4, 5)
// list: List[Int] = List(1, 2, 3, 4, 5)
// 你不能修改已有元素
// list(0) = 10 // 编译错误:List是不可变的
// 所有"修改"操作都返回新List
val doubled = list.map(_ * 2)
// doubled: List(2, 4, 6, 8, 10)
// 原List不变
println(list)
// List(1, 2, 3, 4, 5)
List是链表结构
scala
// Scala List是单向链表
// List(1, 2, 3) = 1 :: 2 :: 3 :: Nil
val list = 1 :: 2 :: 3 :: Nil
// List(1, 2, 3)
// head:第一个元素
list.head // 1
// tail:除第一个元素外的所有元素
list.tail // List(2, 3)
// 递归遍历:这是函数式编程的核心模式
def sum(list: List[Int]): Int = list match {
case Nil => 0
case head :: tail => head + sum(tail)
}
// sum(List(1,2,3)) = 6
为什么用不可变List
| 优势 | 说明 |
|---|---|
| 线程安全 | 不可变对象天然线程安全,不需要锁 |
| 可缓存 | 相同内容的List可以共享引用 |
| 可预测 | 不会被意外修改,调试更容易 |
| 函数式友好 | map/filter/reduce不修改原List |
| 结构共享 | 操作List时可以复用大部分结构 |
02 List创建与初始化
用apply方法创建
scala
// 最常用:List.apply
val list = List(1, 2, 3, 4, 5)
// List(1, 2, 3, 4, 5)
// 空List
val empty = List.empty[Int]
// List()
// 用Nil表示空列表
val empty2 = Nil
// List()
用::构造
scala
// cons操作:在头部添加元素
val list = 3 :: 2 :: 1 :: Nil
// List(1, 2, 3)
// 注意:::是右结合的
// 3 :: (2 :: (1 :: Nil))
// 从右往左构造
// 在已有List头部添加元素
val oldList = List(2, 3, 4)
val newList = 1 :: oldList
// List(1, 2, 3, 4)
// oldList不变:List(2, 3, 4)
常用初始化方法
scala
// range:生成范围列表
List.range(1, 10) // List(1,2,3,4,5,6,7,8,9)
List.range(1, 10, 2) // List(1,3,5,7,9),步长2
// fill:填充相同值
List.fill(5)("hello") // List(hello, hello, hello, hello, hello)
// tabulate:按函数生成
List.tabulate(5)(n => n * n)
// List(0, 1, 4, 9, 16)
// 合并列表
List.concat(List(1,2), List(3,4))
// List(1, 2, 3, 4)
03 List常用操作
索引访问与查找
scala
val list = List(10, 20, 30, 40, 50)
// head:第一个元素
list.head // 10
// last:最后一个元素
list.last // 50
// tail:除第一个外的所有元素
list.tail // List(20, 30, 40, 50)
// init:除最后一个外的所有元素
list.init // List(10, 20, 30, 40)
// 按索引访问
list(2) // 30
list.apply(2) // 30,等价于list(2)
// 长度
list.length // 5
list.size // 5
拼接与添加
scala
val a = List(1, 2, 3)
val b = List(4, 5, 6)
// ++:拼接两个List
val c = a ++ b
// List(1, 2, 3, 4, 5, 6)
// :+:在尾部添加元素
val d = a :+ 4
// List(1, 2, 3, 4)
// +::在头部添加元素(等价于::)
val e = 0 +: a
// List(0, 1, 2, 3)
// 注意:++和:+返回新List,原List不变
反转与排序
scala
val list = List(3, 1, 4, 1, 5, 9, 2, 6)
// 反转
list.reverse
// List(6, 2, 9, 5, 1, 4, 1, 3)
// 升序排序
list.sorted
// List(1, 1, 2, 3, 4, 5, 6, 9)
// 降序排序
list.sortWith(_ > _)
// List(9, 6, 5, 4, 3, 2, 1, 1)
// 按指定字段排序
case class Person(name: String, age: Int)
val people = List(Person("Alice", 25), Person("Bob", 20), Person("Charlie", 30))
people.sortBy(_.age)
// 按年龄升序
04 List与模式匹配
模式匹配是Scala List的精髓------用head/tail分解列表,这是函数式编程的核心模式。
基本模式匹配
scala
def describe(list: List[Int]): String = list match {
case Nil => "空列表"
case List(x) => s"单元素列表:$x"
case List(x, y) => s"两个元素:$x, $y"
case head :: tail => s"头元素:$head,尾部长度:${tail.length}"
}
describe(List()) // "空列表"
describe(List(1)) // "单元素列表:1"
describe(List(1, 2)) // "两个元素:1, 2"
describe(List(1, 2, 3)) // "头元素:1,尾部长度:2"
递归处理列表
scala
// 求和
def sum(list: List[Int]): Int = list match {
case Nil => 0
case head :: tail => head + sum(tail)
}
// sum(List(1,2,3,4,5)) = 15
// 求乘积
def product(list: List[Int]): Int = list match {
case Nil => 1
case head :: tail => head * product(tail)
}
// product(List(1,2,3,4,5)) = 120
// 反转列表
def reverse(list: List[Int]): List[Int] = list match {
case Nil => Nil
case head :: tail => reverse(tail) :+ head
}
// reverse(List(1,2,3)) = List(3,2,1)
嵌套模式匹配
scala
// 匹配特定模式
def process(list: List[Int]): String = list match {
case Nil => "空"
case List(x) if x > 0 => s"正数单元素:$x"
case List(x, y) => s"两个元素:$x + $y = ${x+y}"
case head :: second :: rest => s"前两个:$head, $second,剩余${rest.length}个"
case _ => "其他"
}
对Java开发者的启示:Java里处理列表要写for循环、索引、条件判断;Scala里用模式匹配+递归,代码更简洁、更声明式。这是函数式编程的核心思维------不是"一步步怎么做",而是"列表的结构是什么样的"。
05 List与集合操作
List是Scala集合体系的核心,支持所有标准集合操作。
map/filter/reduce
scala
val list = List(1, 2, 3, 4, 5)
// map:转换每个元素
list.map(_ * 2)
// List(2, 4, 6, 8, 10)
// filter:过滤元素
list.filter(_ % 2 == 0)
// List(2, 4)
// reduce:聚合
list.reduce(_ + _)
// 15
// fold:带初始值的聚合
list.fold(0)(_ + _)
// 15
flatMap与组合
scala
val list = List(1, 2, 3)
// flatMap:每个元素生成多个元素,然后扁平化
list.flatMap(x => List(x, x * 10))
// List(1, 10, 2, 20, 3, 30)
// 组合使用
List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.filter(_ % 2 == 0) // List(2, 4, 6, 8, 10)
.map(_ * 10) // List(20, 40, 60, 80, 100)
.reduce(_ + _) // 300
分组与聚合
scala
val words = List("apple", "banana", "avocado", "blueberry", "cherry")
// 按首字母分组
words.groupBy(_.head)
// Map(a -> List(apple, avocado), b -> List(banana, blueberry), c -> List(cherry))
// 按长度分组
words.groupBy(_.length)
// Map(5 -> List(apple), 6 -> List(banana, cherry), 7 -> List(avocado), 9 -> List(blueberry))
// 计数
words.count(_.startsWith("a")) // 2
大数据场景提示:Spark的RDD转换操作(map/filter/reduceByKey/groupByKey)本质上就是分布式版的List操作。理解List的函数式操作,就是理解Spark数据处理的思维模型。
06 List与Array/ArrayBuffer互转
List → Array
scala
val list = List(1, 2, 3, 4, 5)
// 转换为定长数组
val arr = list.toArray
// Array(1, 2, 3, 4, 5)
// 注意:toArray会复制数据
// 修改arr不影响list
arr(0) = 100
// list: List(1, 2, 3, 4, 5) // 不变
List → ArrayBuffer
scala
import scala.collection.mutable.ArrayBuffer
val list = List(1, 2, 3, 4, 5)
// 转换为可变数组
val buf = list.toBuffer
// ArrayBuffer(1, 2, 3, 4, 5)
// 然后可以添加元素
buf += 6
// ArrayBuffer(1, 2, 3, 4, 5, 6)
Array/ArrayBuffer → List
scala
// 从数组转换
val arr = Array(1, 2, 3)
val list = arr.toList
// List(1, 2, 3)
// 从ArrayBuffer转换
import scala.collection.mutable.ArrayBuffer
val buf = ArrayBuffer(4, 5, 6)
val list2 = buf.toList
// List(4, 5, 6)
List与Seq的关系
scala
val list = List(1, 2, 3)
// List是Seq的子类
val seq: Seq[Int] = list
// List(1, 2, 3)
// List也可以当作Iterable
val iterable: Iterable[Int] = list
// List(1, 2, 3)
07 与Java List的对比
理解Scala和Java List的差异,能帮助你避免常见的迁移陷阱。

核心差异对比
| 维度 | Java List (ArrayList) | Scala List |
|---|---|---|
| 可变性 | 可变 | 不可变 |
| 底层结构 | 动态数组 | 单向链表 |
| 添加元素 | add() | :+ 或 +:(返回新List) |
| 删除元素 | remove() | filter(返回新List) |
| 索引访问 | get(i) O(1) | list(i) O(n) |
| 头部添加 | add(0, e) O(n) | :: O(1) |
| 尾部添加 | add(e) 摊销O(1) | :+ O(n) |
| 遍历 | for-each 或 Iterator | foreach / for推导式 |
| 模式匹配 | 无 | head :: tail |
| 线程安全 | 需同步 | 天然线程安全 |
| 函数式操作 | Java 8 Stream | map/filter/reduce直接用 |
Java List vs Scala List
java
// Java: ArrayList操作
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.remove("b");
for (String s : list) {
System.out.println(s);
}
list.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
scala
// Scala: List操作
val list = List("a", "b", "c")
// 添加元素(返回新List)
val list2 = list :+ "d"
// List(a, b, c, d)
// 过滤(返回新List)
val list3 = list.filter(_ != "b")
// List(a, c)
// 遍历
list.foreach(println)
// 函数式操作
list.map(_.toUpperCase)
// List(A, B, C)
什么时候用List而不是Array
scala
// 不推荐:用Array做频繁的头部插入
var arr = Array(1, 2, 3)
// 每次头部插入都要移动所有元素,O(n)
// 推荐:用List做频繁的头部插入
val list = List(1, 2, 3)
val newList = 0 :: list
// O(1),结构共享
// 经验法则:
// - 频繁头部插入/删除 → List(链表O(1))
// - 频繁随机访问 → Array(数组O(1))
// - 函数式处理、不可变 → List
// - 大数据分布式、collect结果 → Array
写在最后:不可变List是函数式编程的基石
Scala的List,表面上是Java List的"Scala版",本质上是函数式编程的核心数据结构------不可变的链表,配合模式匹配和递归,构成了声明式数据处理的基础。
这种设计哲学带来三个好处:
- 天然线程安全:不可变对象不需要锁,在多线程环境下可以安全共享
- 结构共享:操作List时可以复用大部分结构,性能更好
- 函数式友好:map/filter/reduce不修改原List,可以组合、可以嵌套
这些特性不是孤立的,它们共同支撑了Scala的函数式编程范式。Spark的RDD转换、Flink的数据流处理、AI管道的数据预处理------这些框架的底层都有List思维的影子。
学习建议:不要把List当作"不可变的ArrayList"来学。理解它在函数式编程中的位置------它是一个递归数据结构,配合模式匹配和递归,构成了声明式数据处理的基础。当你习惯了用head/tail分解列表、用map/filter转换列表、用模式匹配处理列表结构,你就掌握了函数式编程的精髓。
从Java的可变List一路用到黑,到Scala的不可变List+模式匹配,不是学习"另一种列表",而是学习一种"更声明式、更安全、更函数式的数据处理方式"。当你习惯了用::构造列表、用head/tail递归、用map/filter转换,你会发现:原来列表处理可以这么优雅。