我有一个流列表,在调用它们的next()
时会随机休眠一段时间,然后从不同的来源读取一个字符。
我正在尝试编写一个将继续调用这些流的消费者,直到EOF
并在运行时构建这些流的公共字典。
到目前为止,我使用ConcurrentHashMap
作为字典,只是为每个流使用者创建一个新线程。
虽然我的解决方案有效,但它似乎非常简单,我想知道是否有更好的用途,例如monix
或fs2
答案 0 :(得分:1)
根据问题的描述和后续评论,我假设存在多个Iterator[Char]
来源:
val allSources : Iterable[Iterator[Char]] = ???
问题是:如何从这些迭代器中同时收集String
值以形成String到count的映射。
基于流的解决方案
首先,我们需要根据分隔符将每个迭代器转换为字符串值的迭代器:
trait Word {
val data : String
}
object EmptyWord extends Word {
override val data = ""
}
case class PartialWord(val data : String) extends Word
case class WholeWord(val data : String) extends Word
val appendToWord : Char => (Word, Char) => Word =
(separator) => (originalWord, appendChar) => originalWord match {
case PartialWord(d) =>
if(appendChar == separator)
WholeWord(d)
else
PartialWord(d + appendChar)
case _ => PartialWord(appendChar.toString)
}
val isWholeWord : Word => Boolean = (_ : Word) match {
case _ : WholeWord => true
case _ => false
}
//using space as separator
val convertCharIterator : Iterator[Char] => Iterator[String] =
(_ : Iterator[Char])
.scanLeft(EmptyWord)(appendToWord(' '))
.filter(isWholeWord)
.map(_.data)
我们现在可以转换所有迭代器来生成字符串,我们可以将所有迭代器组合成一个迭代器:
val allWordSource : Iterator[String] =
allSources.map(convertCharIterator)
.reduceOption( _ ++ _)
.getOrElse(Iterator.empty[String])
此Iterator现在可以作为计算您的计数的akka流的来源:
val addToCounter : (Map[String, Int], String) => Map[String, Int] =
(counter, word) =>
counter.updated(word, counter.getOrElse(word, 0) + 1)
val counter : Future[Map[String, Int]] =
Source
.fromIterator( () => allWordSource)
.runFold(Map.empty[String, Int])(addToCounter)