高效的序列转换为String

时间:2017-02-23 16:55:35

标签: performance scala out-of-memory

我有一个字符串序列Seq[String],表示stdin输入行。

这些行映射到模型实体,但不保证1行= 1个实体实例。

每个实体都用一个特殊字符串分隔,该字符串不会出现在输入中的任何其他位置。

我的解决方案如下:

val entities = lines.mkString.split(myDelimiter).map(parseEntity)

parseEntity实现不相关,它获取一个String并映射到表示模型实体的案例类

问题在于给定的输入,我在lines.mkString上得到OutOfMemoryException。折叠/ foldLeft / foldRight会更有效吗?或者你有更好的选择吗?

2 个答案:

答案 0 :(得分:2)

您可以使用akka流和分隔符框架来解决此问题。有关基本方法,请参阅文档的this section

import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{Framing, Source}
import akka.util.ByteString

val example = (0 until 100).mkString("delimiter").grouped(8).toIndexedSeq
val framing = Framing.delimiter(ByteString("delimiter"), 1000)

implicit val system = ActorSystem()

implicit val mat = ActorMaterializer()

Source(example)
  .map(ByteString.apply)
  .via(framing)
  .map(_.utf8String)
  .runForeach(println)

与ByteString之间的转换有点烦人,但Framing.delimiter仅为ByteString定义。

如果您使用更纯粹的功能方法,fs2也会提供原语来解决这个问题。

答案 1 :(得分:0)

如果你正在从一个小溪中读书,那对我有用的东西(你的里程可能会有所不同)。 Scala LineIterator的略微修改版本:

class EntityIterator(val iter: BufferedIterator[Char]) extends AbstractIterator[String] with Iterator[String] {
  private[this] val sb = new StringBuilder

  def getc() = iter.hasNext && {
    val ch = iter.next
    if (ch == '\n') false // Replace with your delimiter here
    else {
      sb append ch
      true
    }
  }

  def hasNext = iter.hasNext

  def next = {
    sb.clear
    while (getc()) { }
    sb.toString
  }
}

val entities =
  new EnityIterator(scala.io.Source.fromInputStream(...).iter.buffered)

entities.map(...)