【问题标题】:Efficient groupwise aggregation on Scala collectionsScala 集合上的高效分组聚合
【发布时间】:2013-03-20 16:49:39
【问题描述】:

我经常需要做类似的事情

coll.groupBy(f(_)).mapValues(_.foldLeft(x)(g(_,_)))

实现相同效果的最佳方法是什么,但避免使用groupBy 显式构造中间集合?

【问题讨论】:

  • @sschaef 你能解释一下将“什么是最好的方法”更改为“有可能吗?如果有的话怎么做”的原因?它必须是可能的(图灵完备性),并且很容易找到一种笨拙的方法来做到这一点。我也让这个问题不合语法
  • “什么是最好的方法”是一个错误的问题格式,通常无法明确回答。但是我在再次考虑编辑后同意回滚,它并没有使问题变得更好。

标签: scala scala-collections


【解决方案1】:

您可以将初始集合折叠在包含中间结果的地图上:

def groupFold[A,B,X](as: Iterable[A], f: A => B, init: X, g: (X,A) => X): Map[B,X] = 
  as.foldLeft(Map[B,X]().withDefaultValue(init)){
    case (m,a) => {
      val key = f(a)
      m.updated(key, g(m(key),a))
    }
  }

你说收藏,我写了Iterable,但你必须考虑在你的问题中顺序是否重要。

如果您想要高效的代码,您可能会使用 Rex 的答案中的可变映射。

【讨论】:

  • 如果我没记错的话,您可以将m :+ m.get(f(a)).map(g(_,a)).getOrElse(g(init,a)) 简化为m :+ m.getOrElse(f(a), init).map(g(_,a))
  • @johnsullivan 谢谢,这样更好。
  • 请注意,虽然这样可以节省内存,但实际上比原来的要慢。
【解决方案2】:

你不能真正把它当作一个单行线来做,所以在写这样更复杂的东西之前你应该确定你需要它(因为你要求“高效”,所以从有点注重性能的角度写):

final case class Var[A](var value: A) { }
def multifold[A,B,C](xs: Traversable[A])(f: A => B)(zero: C)(g: (C,A) => C) = {
  import scala.collection.JavaConverters._
  val m = new java.util.HashMap[B, Var[C]]
  xs.foreach{ x =>
    val v = { 
      val fx = f(x)
      val op = m.get(fx)
      if (op != null) op
      else { val nv = Var(zero); m.put(fx, nv); nv }
    }
    v.value = g(v.value, x)
  }
  m.asScala.mapValues(_.value)
}

(根据您的用例,您可能希望在最后一步打包到不可变映射中。)这是一个实际操作的示例:

scala> multifold(List("salmon","herring","haddock"))(_(0))(0)(_ + _.length)
res1: scala.collection.mutable.HashMap[Char,Int] = Map(h -> 14, s -> 6)        

现在,您可能会注意到这里有些奇怪:我使用的是 Java HashMap。这是因为 Java 的 HashMap 比 Scala 快 2-3 倍。 (您可以使用 Scala HashMap 编写等效的东西,但它实际上并没有使事情比您原来的更快。)因此,此操作比您发布的快 2-3 倍。但除非您承受着严重的内存压力,否则创建瞬态集合实际上并不会对您造成太大伤害。

【讨论】:

  • 谢谢!我的主要问题是内存。我处理非常大的收藏。对于 iinput 集合,我可以使用某种惰性或非核心实现,但这对中间的集合并没有真正的帮助。
  • 如果你关心内存,你可以查看 trove java collections library,它提供了特殊的原语集合。
  • @Rex Kerr 是 hashmap 实现中插入、检索或两者的 3 倍速度差异?
  • @DanielMahler - 在我手中插入时会产生更大的差异,但它们都在 2-3 倍范围内,具体取决于各种难以确定的因素(处理器缓存、JIT编译效果等)。
猜你喜欢
  • 2013-01-26
  • 1970-01-01
  • 2019-03-15
  • 2014-10-08
  • 1970-01-01
  • 2018-06-24
  • 2021-03-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多