1. 概述
在编程中,我们有时需要把多个元素组合在一起,这些元素之间可能有关联,也可能没有。为了应对这种场景,可以使用元组(Tuple) —— 一种有序的对象集合,支持不同类型的数据。
之前我们介绍过一个 Java 的第三方库 javatuples,它提供了对元组的支持。而在 Scala 中,元组是语言内置的特性,属于不可变数据结构。
✅ 本文将带你了解如何在 Scala 中创建元组、访问其元素,并介绍一些常用的元组方法以及适用场景。
2. 创建元组
创建元组非常简单,不需要任何模板代码。只需要把元素用圆括号括起来即可:
val tuple: (String, Int) = ("Joe", 34)
上面这行代码创建了一个包含 String
和 Int
类型元素的元组。
✅ 也可以使用箭头语法创建二元组(Pair):
val stillTuple: (String, Int) = "Joe" -> 34
在 Scala 2 中,元组由一系列类实现:从 Tuple2
到 Tuple22
。通常我们不会直接使用这些类,但了解它们的存在有助于理解底层机制。
⚠️ 注意:Scala 2 中最多支持 22 个元素的元组。超过这个限制会编译报错。
✅ 在 Scala 3 中,这个限制被移除了,引入了 TupleXXL
,可以支持超过 22 个元素的元组。
创建更多元素的元组也很简单:
val tuple3: (String, Int, Boolean) = ("Joe", 34, true)
val tuple4: (String, Int, Boolean, Char) = ("Joe", 34, true, 'A')
📌 注意:(String, Int, Boolean)
是 Tuple3[String, Int, Boolean]
的语法糖。
3. 访问元组中的元素
访问元组元素有两种常见方式:
✅ 方式一:使用下标访问(通过 _1
, _2
等)
val name = tuple._1
val age = tuple._2
✅ 方式二(Scala 3 新增):像访问 List 或 Array 一样使用索引
val name = tuple(0)
✅ 方式三:使用模式匹配解构赋值
val (userName, userAge) = tuple
这会将元组的第一个元素赋值给 userName
(类型为 String
),第二个元素赋给 userAge
(类型为 Int
)。
⚠️ 如果你只关心部分元素,可以用下划线 _
忽略其他值:
val (_, myAge) = tuple
4. 常见使用场景
元组在需要返回多个值或传递多个值时非常有用,特别是当这些值之间没有强关联、不值得定义一个 case class 时。
4.1. 返回多个值
一个典型例子是 partition
方法:
def partition[A](xs: List[A])(predicate: A => Boolean): (List[A], List[A]) = {
xs.foldRight((List.empty[A], List.empty[A])) {
case (a, (lefts, rights)) =>
if (predicate(a)) (a :: lefts, rights) else (lefts, a :: rights)
}
}
val (evens, odds) = partition(List(1, 3, 4, 5, 2))(_ % 2 == 0)
这个方法返回两个列表:一个包含满足条件的元素,另一个包含不满足的。
4.2. 传递多个参数
可以将元组作为参数传入函数。Scala 提供了 tupled()
方法,用于将多参数函数转换为接受元组的函数:
val data = Map(
"Joe" -> 34,
"Mike" -> 16,
"Kelly" -> 21
)
case class User(name: String, isAdult: Boolean)
val createUser: (String, Int) => User = (name, age) => User(name, age >= 18)
val users = data.map(createUser.tupled)
如果忘记调用 tupled()
,会报类型不匹配的错误:
type mismatch;
found : (String, Int) => User
required: ((String, Int)) => ?
val users = data.map(createUser)
编译器提示我们:试图把两个参数传给一个只接受元组参数的函数。
5. Scala 3 中的元组增强
✅ Scala 3 对元组进行了大幅增强,使其行为更接近于一个支持索引的集合。
一些新特性如下:
val tuple = (1, "Baeldung", false)
assert(tuple(0) == 1)
assert(tuple.head == 1)
assert(tuple.tail == ("Baeldung", false))
assert(tuple.last == false)
assert(tuple.take(2) == (1, "Baeldung"))
assert(tuple.toList == List(1, "Baeldung", false))
assert(tuple ++ ("Hello", "World") == (1, "Baeldung", false, "Hello", "World"))
✅ 此外,Scala 3 引入了 Match Type 特性来支持这些功能。
还有一个很实用的功能:从 case class 生成元组:
final case class User(id: Long, name: String, active: Boolean)
val baeldung = User(1, "baeldung", true)
val userTuple = Tuple.fromProductTyped(baeldung)
assert(userTuple == (1, "baeldung", true))
6. 总结
在这篇文章中,我们学习了 Scala 中的元组类型,包括如何创建、访问元素,以及一些常用的元组方法和典型使用场景。
✅ 我们还介绍了 Scala 3 中对元组的重大改进,使其功能更加强大和灵活。
📌 源码地址: