Spark/Scala迭代器无法分配在foreach循环外部定义的变量
请注意:尽管这个问题提到了Spark(2.1),但我认为这实际上是一个Scala(2.11)的核心问题,任何精通Scala的开发人员都能够回答Spark/Scala迭代器无法分配在foreach循环外部定义的变量,scala,apache-spark,iterator,Scala,Apache Spark,Iterator,请注意:尽管这个问题提到了Spark(2.1),但我认为这实际上是一个Scala(2.11)的核心问题,任何精通Scala的开发人员都能够回答 我有下面的代码创建一个Spark(基本上是一个2D表)并逐行迭代它。如果特定行的username列的值为“fizzbuzz”,则我希望设置在迭代器外部定义的变量,并在行迭代完成后使用该变量: val myDataset = sqlContext .read .format("org.apache.spark.sql.cassandr
我有下面的代码创建一个Spark(基本上是一个2D表)并逐行迭代它。如果特定行的
username
列的值为“fizzbuzz”,则我希望设置在迭代器外部定义的变量,并在行迭代完成后使用该变量:
val myDataset = sqlContext
.read
.format("org.apache.spark.sql.cassandra")
.options(Map("table" -> "mytable", "keyspace" -> "mykeyspace"))
.load()
var foobar : String
myDataset.collect().foreach(rec =>
if(rec.getAs("username") == "fizzbuzz") {
foobar = rec.getAs("foobarval")
}
)
if(foobar == null) {
throw new Exception("The fizzbuzz user was not found.")
}
当我运行此命令时,会出现以下异常:
error: class $iw needs to be abstract, since:
it has 2 unimplemented members.
/** As seen from class $iw, the missing signatures are as follows.
* For convenience, these are usable as stub implementations.
*/
def foobar=(x$1: String): Unit = ???
class $iw extends Serializable {
^
有什么特殊原因导致我这样做吗?
foobar
变量应该初始化:
var foobar: String = null
此外,这看起来也不对:
foobar = rec.getAs("foobarval")
应该是:
foobar = rec.getAs[String]("foobarval")
总的来说,这不是一条路要走。它根本不受益于Spark执行模型。我会过滤并取而代之:
myDataset.filter($"username" === "fizzbuzz").select("foobarval").take(1)
foobar
变量应初始化:
var foobar: String = null
此外,这看起来也不对:
foobar = rec.getAs("foobarval")
应该是:
foobar = rec.getAs[String]("foobarval")
总的来说,这不是一条路要走。它根本不受益于Spark执行模型。我会过滤并取而代之:
myDataset.filter($"username" === "fizzbuzz").select("foobarval").take(1)
在方法或非抽象类中,必须为每个变量定义一个值;在这里,您将
foobar
保留为未定义。如果您将其定义为初始值为null
,则一切都会按预期进行:
var foobar: String = null
但是:请注意,您的代码既不惯用(不遵循Scala和Spark的最佳实践),又有潜在的风险/速度慢:
- 您应该避免使用易变值,例如
——不可变代码更容易推理,并且可以让您真正利用Scala的强大功能foobar
- 除非您确定数据帧非常小,否则应避免在数据帧上调用
,因为collect
会将所有数据从工作节点(可能有很多)收集到单个驱动程序节点,这会很慢,并可能导致collect
OutOfMemoryError
- 不鼓励使用
(因为它经常导致意外的null
s)NullPointerException
DataFrame.filter
来过滤相关记录,并且可能使用Option
来正确表示可能的空值,例如:
import spark.implicits._
val foobar: Option[String] = myDataset
.filter($"username" === "fizzbuzz") // filter only relevant records
.take(1) // get first 1 record (if it exists) as an Array[Row]
.headOption // get the first item in the array, or None
.map(r => r.getAs[String]("foobarval")) // get the value of the column "foobarval", or None
if (foobar.isEmpty) {
throw new Exception("The fizzbuzz user was not found.")
}
在方法或非抽象类中,必须为每个变量定义一个值;在这里,您将
foobar
保留为未定义。如果您将其定义为初始值为null
,则一切都会按预期进行:
var foobar: String = null
但是:请注意,您的代码既不惯用(不遵循Scala和Spark的最佳实践),又有潜在的风险/速度慢:
- 您应该避免使用易变值,例如
——不可变代码更容易推理,并且可以让您真正利用Scala的强大功能foobar
- 除非您确定数据帧非常小,否则应避免在数据帧上调用
,因为collect
会将所有数据从工作节点(可能有很多)收集到单个驱动程序节点,这会很慢,并可能导致collect
OutOfMemoryError
- 不鼓励使用
(因为它经常导致意外的null
s)NullPointerException
DataFrame.filter
来过滤相关记录,并且可能使用Option
来正确表示可能的空值,例如:
import spark.implicits._
val foobar: Option[String] = myDataset
.filter($"username" === "fizzbuzz") // filter only relevant records
.take(1) // get first 1 record (if it exists) as an Array[Row]
.headOption // get the first item in the array, or None
.map(r => r.getAs[String]("foobarval")) // get the value of the column "foobarval", or None
if (foobar.isEmpty) {
throw new Exception("The fizzbuzz user was not found.")
}
您可能应该在数据帧上使用过滤器和选择:
import spark.sqlContext.implicits._
val data = spark.sparkContext.parallelize(List(
"""{ "username": "none", "foobarval":"none" }""",
"""{ "username": "fizzbuzz", "foobarval":"expectedval" }"""))
val df = spark.read.json(data)
val foobar = df.filter($"username" === "fizzbuzz").select($"foobarval").collect.head.getString(0)
您可能应该在数据帧上使用过滤器和选择:
import spark.sqlContext.implicits._
val data = spark.sparkContext.parallelize(List(
"""{ "username": "none", "foobarval":"none" }""",
"""{ "username": "fizzbuzz", "foobarval":"expectedval" }"""))
val df = spark.read.json(data)
val foobar = df.filter($"username" === "fizzbuzz").select($"foobarval").collect.head.getString(0)