Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 要映射的大型数组_Scala_Graph - Fatal编程技术网

Scala 要映射的大型数组

Scala 要映射的大型数组,scala,graph,Scala,Graph,我有一个大的文本文件,每行有2个数字,表示行中第一个和第二个元素之间的定向边。我试图在scala中构建一个图,将其表示为映射[tailOfEdge,arrayOfHeadsofEdge] 如果我的文件 1 2 1 3 2 3 这应该是Map(1->Array(2,3),2->Array(3)) 但是,我的文件非常大(约500万行) 我最初尝试读取整个文件,使用toArray,然后使用groupBy并以这种方式累积。但是,我一直遇到堆大小的问题(更不用说他的方法可能

我有一个大的文本文件,每行有2个数字,表示行中第一个和第二个元素之间的定向边。我试图在scala中构建一个图,将其表示为
映射[tailOfEdge,arrayOfHeadsofEdge]

如果我的文件

   1   2
   1   3
   2   3
这应该是
Map(1->Array(2,3),2->Array(3))

但是,我的文件非常大(约500万行)

我最初尝试读取整个文件,使用
toArray
,然后使用
groupBy
并以这种方式累积。但是,我一直遇到堆大小的问题(更不用说他的方法可能很幼稚)

现在,对我来说有效的方法(尽管速度非常慢)是创建一个可变映射,循环文件的每一行(使用For循环),将该行拆分为2个数字。给定节点的所有边在文件中都是连续的,因此我只需跟踪所需的节点,如果是同一个节点,则累积新边,如果是新节点,则将完成的累积数组添加到映射中,重置所需的节点,并使用此新列表重新启动累积数组


当然有更好的方法可以做到这一点……

你可以用一个左折叠和一个不变的映射非常干净地做到这一点:

val source = scala.io.Source.fromFile(args(0))

val graph = source.getLines.foldLeft[Map[Int, Vector[Int]]](
  Map.empty withDefaultValue Vector.empty
) {
  case (acc, line) => line.trim.split("\\s+").map(_.toInt) match {
    case Array(k, v) => acc.updated(k, acc(k) :+ v)
  }
}

source.close()
在我的机器上,它在大约7秒内运行在一个包含500万行的文件上
getLines
是一个迭代器,因此不需要将整个文件读入内存

我不知道“难以置信的慢”对你意味着什么。这个实现不会对文件中键的顺序做出任何假设,如果您真的需要它的速度超过每秒一百万行,那么您应该能够利用它们是按顺序排列的这一事实。但这可能不会有多大帮助,而且几乎肯定会涉及到更复杂的代码


你也可以使用数组而不是向量——我刚才用向量来说明你甚至不需要头部列表就可以变。

如果你的输入序列真的很大,另一个解决方案最终会出现。下面是我的强制解决方案,它依赖于调用方在组生成时干净地处理组,但AFAICT以常量堆栈运行,并保留最小的堆供自己使用。:)

希望其他人能想出一个折叠的流或类似的东西,将有类似的性能特点,只要你小心不要保留对头部的引用

/**
 * @param in       the input
 * @param disposal a function that will dispose of groups as they're identified
 */
def groupByInfinite[A,B](in: Iterator[(A,B)])(disposal: (A,Seq[B]) => Unit) {

  /**
   * @param in      the input
   * @param current the current A value
   * @param got     the B values being accumulated for the current A value
   */
  @tailrec
  def group0(in: Iterator[(A,B)], current: A, got: Seq[B]) {
    if (in.hasNext) {
      val (a,b) = in.next()
      if (a == current) {
        group0(in, a, got :+ b)
      } else {
        disposal(current, got)
        group0(in, a, Vector(b))
      }
    } else {
      disposal(current, got)
    }
  }

  if (in.hasNext) {
    val (a,b) = in.next()
    group0(in, a, Vector(b))
  }
}

从性能的角度来看,您正在做的事情听起来非常接近最优。它不应该太慢。你能发布代码吗?@RüdigerKlaehn谢谢你的回复。我将使用下面发布的foldLeft代码。谢谢嗯。我没有得到那种表现。我不知道这是不是我的机器。稍后我将在另一台计算机上重试……作为参考,我使用的是运行Arch Linux和OpenJDK的JDK 7的老化ThinkPad。示例文件是500万对介于0和100之间的随机数。我实际上得到了一些奇怪的行为,但可能就是这个文件
scala>val source=scala.io.source.fromFile(“./SCC.txt”)
source:scala.io.BufferedSource=non-empty迭代器
scala>source.getLines.take(2).toList
res9:List[String]=List(“94”,“72 28395”)然后
scala>val l=for(line l.filter(!=2).toList
res14:List[Int]=列表(1)但是不应该有这样的数字。我意识到我从来没有接受过这一点,尽管我使用过它。我的错!如果目标是构建一个地图,我不知道这里的内存使用情况与左折叠解决方案有什么显著的不同。是的,你是对的,但有时能够处理数据比使用f更重要将其转换为一些连续的数据结构。:)