Scala 要映射的大型数组
我有一个大的文本文件,每行有2个数字,表示行中第一个和第二个元素之间的定向边。我试图在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并以这种方式累积。但是,我一直遇到堆大小的问题(更不用说他的方法可能
映射[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更重要将其转换为一些连续的数据结构。:)