Spark/Scala迭代器无法分配在foreach循环外部定义的变量

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(2.1),但我认为这实际上是一个Scala(2.11)的核心问题,任何精通Scala的开发人员都能够回答


我有下面的代码创建一个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的最佳实践),又有潜在的风险/速度慢:

  • 您应该避免使用易变值,例如
    foobar
    ——不可变代码更容易推理,并且可以让您真正利用Scala的强大功能
  • 除非您确定数据帧非常小,否则应避免在数据帧上调用
    collect
    ,因为
    collect
    会将所有数据从工作节点(可能有很多)收集到单个驱动程序节点,这会很慢,并可能导致
    OutOfMemoryError
  • 不鼓励使用
    null
    (因为它经常导致意外的
    NullPointerException
    s)
此代码的更惯用版本将使用
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的最佳实践),又有潜在的风险/速度慢:

  • 您应该避免使用易变值,例如
    foobar
    ——不可变代码更容易推理,并且可以让您真正利用Scala的强大功能
  • 除非您确定数据帧非常小,否则应避免在数据帧上调用
    collect
    ,因为
    collect
    会将所有数据从工作节点(可能有很多)收集到单个驱动程序节点,这会很慢,并可能导致
    OutOfMemoryError
  • 不鼓励使用
    null
    (因为它经常导致意外的
    NullPointerException
    s)
此代码的更惯用版本将使用
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)