一句话概括,二者的关系就是
DataFrame = Dataset[Row]
就是说 DataFrame是 Dataset泛型为Row的一种特例,而Dataset的泛型还可以是其他东西,比如自定义类Person等等
object DataSetDemo1 extends App {
val spark: SparkSession = SparkSession.builder()
.appName("SparkSQL")
.master("local")
.getOrCreate()
import spark.implicits._
private val girls = Seq(
Girl(1, "李伟", 1, 180.0),
Girl(2, "汪松伟", 2, 179.0),
Girl(3, "常洪浩", 1, 183.0),
Girl(4, "麻宁娜", 0, 168.0)
)
//Dataset[Girl]中的泛型可以为任意类型,可以是自定义类型
//DataSet不仅仅知道字段,而且知道字段类型,所以有更严格的错误检查
//样例类被用来在Dataset中定义数据的结构信息,样例类中每个属性的名称直接映射到DataSet中的字段名称
private val ds: Dataset[Girl] = spark.createDataset(girls)
private val ds1: Array[Girl] = ds.collect()
//循环遍历取出ds中的Girl对象
for (x <- ds1) {
println(x)
println(x.height)
}
//DataFrame=Dataset[Row].就是说df是ds的一种特殊情况,特殊在泛型是个ROW.
private val df: Dataset[Row] = spark.createDataFrame(girls)
private val df1: Array[Row] = df.collect()
//遍历取出df中的对象,只能利用Row对象的一些方法取出
//DataFrame只是知道字段,但是不知道字段的类型,所以在执行这些操作的时候是没办法在编译的时候检查是否类型失败的,比如你可以对一个String进行减法操作,在执行的时候才报错
for (x <- df1) {
println(x)
println(x.getAs("height"))
}
//df可以通过as转为ds对象,其实就是用一个具体的类换掉泛型Row
private val value: Dataset[Girl] = df.as[Girl]
private val df2ds: Array[Girl] = value.collect()
for(x<-df2ds){
println(x)
}
//至于ds转df,则更为简单,泛型换位Row就可以了,这个过程中比如丢掉了一些信息
private val value1: DataFrame = ds.toDF()
private val ds2df: Array[Row] = value1.collect()
spark.stop()
}
case class Girl(id: Int, name: String, sex: Int, height: Double)
总结
- df转ds,通过as,传入一个具体的泛型
- ds转df,toDF方法,这个方向更容易
- 正常情况下优先用ds,因为知道是个什么东西,要是写成df,就是个Row对象.就用起来不太方便
- 如果要写一些适配性很强的函数时,如果使用Dataset,行的类型又不确定,可能是各种case class,无法实现适配,这时候用DataFrame即Dataset[Row]就能比较好的解决问题