以下scala(spark 1.6)代码,用于读取一行的值时,当值为null时,用 NullPointerException
失败。
val test = row.getAs[Int]("ColumnName").toString
虽然效果很好
val test1 = row.getAs[Int]("ColumnName") // returns 0 for null
val test2 = test1.toString // converts to String fine
是什么引起NullPointerException
,建议处理此类情况的建议方法是什么?
ps:从数据框中获取如下:
val myRDD = myDF.repartition(partitions)
.mapPartitions{ rows =>
rows.flatMap{ row =>
functionWithRows(row) //has above logic to read null column which fails
}
}
functionWithRows
然后上面提到的 NullPointerException
。
MyDF模式:
root
|-- LDID: string (nullable = true)
|-- KTAG: string (nullable = true)
|-- ColumnName: integer (nullable = true)
getAs
定义为:
def getAs[T](i: Int): T = get(i).asInstanceOf[T]
,当我们进行toString时,我们调用不取决于类型的Object.toString
,因此asInstanceOf[T]
被编译器删除,即。
row.getAs[Int](0).toString -> row.get(0).toString
我们可以通过编写简单的Scala代码来确认:
import org.apache.spark.sql._
object Test {
val row = Row(null)
row.getAs[Int](0).toString
}
然后对其进行编译:
$ scalac -classpath $SPARK_HOME/jars/'*' -print test.scala
[[syntax trees at end of cleanup]] // test.scala
package <empty> {
object Test extends Object {
private[this] val row: org.apache.spark.sql.Row = _;
<stable> <accessor> def row(): org.apache.spark.sql.Row = Test.this.row;
def <init>(): Test.type = {
Test.super.<init>();
Test.this.row = org.apache.spark.sql.Row.apply(scala.this.Predef.genericWrapArray(Array[Object]{null}));
Test.this.row().getAs(0).toString();
()
}
}
}
,正确的方法将是:
String.valueOf(row.getAs[Int](0))
为了避免零值,这是一个更好的做法是在检查之前使用isNullAt
,因为文档建议:
getas
<T> T getAs(int i)
返回位置
i
处的值。对于原始类型,如果值为null 返回特定于原始IE的"零值"。0
用于Int
-使用isNullAt
确保值不是null
我同意这种行为令人困惑。