Java Apache Spark Accumulable addInPlace需要返回R1?或者有什么价值?

Java Apache Spark Accumulable addInPlace需要返回R1?或者有什么价值?,java,scala,apache-spark,return,rdd,Java,Scala,Apache Spark,Return,Rdd,要合并来自不同分区的相同可累积值,请执行以下操作: /** * Merge two accumulated values together. Is allowed to modify and return the first value * for efficiency (to avoid allocating objects). * * @param r1 one set of accumulated data * @param r2 another set of accumulat

要合并来自不同分区的相同可累积值,请执行以下操作:

/**
 * Merge two accumulated values together. Is allowed to modify and return the first value
 * for efficiency (to avoid allocating objects).
 *
 * @param r1 one set of accumulated data
 * @param r2 another set of accumulated data
 * @return both data sets merged together
 */
def addInPlace(r1: R, r2: R): R
我假设在AccumulableParam实现中定义addInPlace时可以返回任何想要的值。我假设作为r1传入的任何指针都将指向我返回的任何指针

我的老板认为r1传入是返回语句中唯一允许的内容。这听起来像安·兰德斯,谁说得对

有一种情况,我只想扔掉r1,用r2中的对象替换它,这将是这个合并累加器的新值


我可以直接返回r2吗?或者我必须按照我的老板(Java编程经验要丰富得多)的想法对r1进行深度复制吗?要清楚,Spark当然是用Scala编写的,我正在编写一个用Java实现AccumulableParam的类。

根据经验,在执行折叠操作时,您应该永远不要修改第二个参数。我们可以用一个简单的例子来说明原因。假设我们有这样的简单累加器:

import org.apache.spark.AccumulatorParam
import scala.collection.mutable.{Map => MMap}

type ACC = MMap[String, Int]

object DummyAccumulatorParam extends AccumulatorParam[ACC] {
  def zero(initialValue: ACC): ACC = {
    initialValue
  }

  def addInPlace(acc: ACC, v: ACC): ACC = {
    v("x") = acc.getOrElse("x", 0) +  v.getOrElse("x", 0)
    v
  }
}
它特别有用,但没关系。重点是它修改了第二个参数。让我们看看它是否有效:

val rdd = sc.parallelize(Seq(MMap("x" -> 1), MMap("x" -> 1), MMap("x" -> 1)), 1)

val accum1 = sc.accumulator(MMap("x" -> 0))(DummyAccumulatorParam)
rdd.foreach(x => accum1 += x)

accum1.value
// scala.collection.mutable.Map[String,Int] = Map(x -> 3)
到目前为止还不错。我们甚至可以创建另一个,它可以按预期工作:

val accum2 = sc.accumulator(MMap("x" -> 0))(DummyAccumulatorParam)
rdd.foreach(x => accum2 += x)

accum2.value
// scala.collection.mutable.Map[String,Int] = Map(x -> 3)
现在让我们缓存数据:

rdd.cache
重复此过程:

val accum3 = sc.accumulator(MMap("x" -> 0))(DummyAccumulatorParam)
rdd.foreach(x => accum3 += x)

val accum4 = sc.accumulator(MMap("x" -> 0))(DummyAccumulatorParam)
rdd.foreach(x => accum4 += x)
并检查累加器值:

accum4.value
// scala.collection.mutable.Map[String,Int] = Map(x -> 6)
和RDD内容:

rdd.collect
// Array[scala.collection.mutable.Map[String,Int]] = 
//  Array(Map(x -> 1), Map(x -> 3), Map(x -> 6))

因此,正如您所看到的,返回或修改第二个参数是不安全的。它也适用于其他类似的操作,如
折叠
聚合

非常棒!同时,这给我留下了几个问题。首先,这是否与
aggregate()
是一种转换有关,在这种转换中,我们不能假设累加器只运行一次,即使是第一个参数?我认为
foreach()
是一种安全(基于操作)的方法,可以让累加器在每个任务中只运行一次?尽管我认为你提出了不同的观点。第二,我看到你在适当的地方修改RDD了吗???我认为RDD是不可变的,还是过于简单化了?如果我认为我可以可靠地修改RDD元素,我不会使用累加器:)您的RDD以(1,1,1)开头,以(1,3,6)结尾,对吗?不变的数据结构并不意味着不变的数据。如果不可变数据结构包含可变对象,则这些对象可能会更改。它们根本不能被另一组对象所取代。但是,在Spark中对数据进行变异是一个严重的编程错误,在这里仅用于说明为什么不应该对第二个参数进行变异/返回。非常感谢您的澄清,听到您这样说让我感觉更好,因为我一直在告诉我的老板,函数式编程通常不允许修改现有的内容,而且他并不总是相信我,因为他有着极其卓越的Java经验。我再次热爱我对Scala thx的了解,因为我回答了这些模糊的问题,这些问题与其他更标准的编程q不同。