Scala中如何将Tuple作为MapType的keyType?
解决Spark MapType使用Tuple作为keyType的问题
没问题,我来给你捋清楚怎么在Spark的MapType里用Tuple当key!Spark本身确实没有把Tuple作为原生的DataType子类,但我们可以用StructType来完美模拟Tuple的结构,因为StructType是支持作为MapType的keyType的,而且两者的特性高度匹配——都是固定长度、由不同类型元素组成的集合。
具体步骤如下:
1. 用StructType定义对应Tuple的key类型
比如你想用(String, Int)这样的二元Tuple当Map的key,就定义一个包含对应字段的StructType:
import org.apache.spark.sql.types._ // 对应Scala Tuple(String, Int)的StructType val tupleKeySchema = StructType(Seq( StructField("_1", StringType, nullable = false), // 对应Tuple的第一个元素 StructField("_2", IntegerType, nullable = false) // 对应Tuple的第二个元素 )) // 定义MapType:key是模拟Tuple的StructType,value用StringType举例 val mapSchema = MapType(tupleKeySchema, StringType, valueContainsNull = false)
这里字段名用_1、_2是为了和Scala Tuple的元素命名对应,方便后续理解,你也可以改成更有意义的名字。
2. 构造数据时将Tuple转为Row对象
因为StructType对应的数据载体是Spark的Row对象,所以创建数据时需要把Scala Tuple转换成Row:
import org.apache.spark.sql.Row import org.apache.spark.sql.SparkSession val spark = SparkSession.builder().appName("MapWithTupleKey").master("local[*]").getOrCreate() import spark.implicits._ // 构造包含Struct-key Map的数据集 val data = Seq( ( Map( Row("apple", 1) -> "red", Row("banana", 2) -> "yellow" ) ) ).toDF("fruit_map") // 查看schema确认结构 data.printSchema()
执行后你会看到fruit_map的keyType就是我们定义的StructType,完全对应原来的Tuple结构。
3. 查询和操作Map中的key
当你需要访问Map的key元素时,可以像操作Struct字段一样,通过.来访问对应位置的元素:
// 展开Map的key和value,提取原来Tuple的元素 data.select(explode($"fruit_map").as(Seq("key", "value"))) .select( $"key._1".as("fruit_name"), $"key._2".as("fruit_id"), $"value".as("fruit_color") ) .show()
这样就能轻松获取到原来Tuple的各个元素,和直接操作Tuple的体验几乎一致。
为什么不能直接用Tuple?
Spark的DataType体系只支持原生的Spark数据类型(就是你列出的那些子类),而Scala Tuple是语言层面的集合类型,不属于Spark的原生数据类型范畴。但StructType的设计就是用来表达结构化的复合类型,刚好可以完美替代Tuple作为Map的key,功能上完全覆盖Tuple的需求。
内容的提问来源于stack exchange,提问作者Nicola Vandoni
相关产品推荐
相关产品推荐

