Spark中select使用x.head、x.tail:_*而非直接传数组:_*的原因
问题原因解析
核心是Spark select 方法的重载设计和Scala可变参数的匹配规则共同导致的差异:
1. 先明确Spark select 的两个相关重载签名
Spark的DataFrame没有提供全可变参数的字符串列名版本的select,仅有的两个相关重载为:
- 字符串列名版本:
def select(col: String, cols: String*): DataFrame
要求必须传入至少1个单独的字符串作为首个列名,后续才可以跟可变长度的字符串列名列表 - Column对象版本:
def select(cols: Column*): DataFrame
支持纯可变长度的Column对象入参,没有首个参数单独传的要求
2. 错误写法的报错原因
你尝试使用dataFrame.select(reorderedColumnNames: _*)时,Scala编译器的重载匹配逻辑如下:
- 首先匹配字符串版本的
select:该重载要求第一个参数是单独的String,你直接展开整个Array[String]作为入参,不符合「首参单独+后续可变」的签名要求,匹配失败 - 尝试匹配Column版本的
select:你传入的展开元素是String类型,无法直接隐式转换为Column对象,因此触发类型不匹配的报错
3. 正确写法的匹配逻辑
dataFrame.select(reorderedColumnNames.head, reorderedColumnNames.tail: _*)的入参结构刚好完全匹配字符串版本的重载:
- 第一个参数
reorderedColumnNames.head是单独的String类型,符合首参要求 - 第二个参数
reorderedColumnNames.tail: _*是数组剩余元素展开的可变长度String列表,符合后续可变参数的要求
因此可以正常执行。
可选替代方案
如果不想拆分head和tail,也可以将字符串数组转为Column数组后传入,匹配Column版本的重载即可:
val result: DataFrame = dataFrame.select(reorderedColumnNames.map(col): _*)
内容的提问来源于stack exchange,提问作者Pablo Ochoa
相关产品推荐
相关产品推荐

