我有一个由Jobs组成的数据结构,每个都包含一组Tasks。作业和任务数据都在以下文件中定义:
jobs.txt:
JA
JB
JC
tasks.txt:
JB T2
JA T1
JC T1
JA T3
JA T2
JB T1
创建对象的过程如下:
- 阅读每个作业,创建它并按照id存储它
- 读取任务,按ID检索作业,创建任务,在作业中存储任务
读取文件后,永远不会修改此数据结构。所以我希望作业中的任务存储在一个不可变的集合中。但我不知道如何以有效的方式做到这一点。 (注意:存储作业的不可变映射可能不可变)
以下是代码的简化版本:
class Task(val id: String)
class Job(val id: String) {
val tasks = collection.mutable.Set[Task]() // This sholud be immutable
}
val jobs = collection.mutable.Map[String, Job]() // This is ok to be mutable
// read jobs
for (line <- io.Source.fromFile("jobs.txt").getLines) {
val job = new Job(line.trim)
jobs += (job.id -> job)
}
// read tasks
for (line <- io.Source.fromFile("tasks.txt").getLines) {
val tokens = line.split("\t")
val job = jobs(tokens(0).trim)
val task = new Task(job.id + "." + tokens(1).trim)
job.tasks += task
}
提前感谢您的每一个建议!
答案 0 :(得分:4)
执行此操作的最有效方法是将所有内容读入可变结构,然后在最后转换为不可变结构,但这可能需要对具有大量字段的类进行大量冗余编码。因此,请考虑使用与基础集合使用的模式相同的模式:具有新任务的作业是新作业。
这是一个甚至懒得阅读作业列表的例子 - 它从任务列表中推断出来。 (这是一个在2.7.x下工作的示例; 2.8的最新版本使用“Source.fromPath”而不是“Source.fromFile”。)
object Example {
class Task(val id: String) {
override def toString = id
}
class Job(val id: String, val tasks: Set[Task]) {
def this(id0: String, old: Option[Job], taskID: String) = {
this(id0 , old.getOrElse(EmptyJob).tasks + new Task(taskID))
}
override def toString = id+" does "+tasks.toString
}
object EmptyJob extends Job("",Set.empty[Task]) { }
def read(fname: String):Map[String,Job] = {
val map = new scala.collection.mutable.HashMap[String,Job]()
scala.io.Source.fromFile(fname).getLines.foreach(line => {
line.split("\t") match {
case Array(j,t) => {
val jobID = j.trim
val taskID = t.trim
map += (jobID -> new Job(jobID,map.get(jobID),taskID))
}
case _ => /* Handle error? */
}
})
new scala.collection.immutable.HashMap() ++ map
}
}
scala> Example.read("tasks.txt")
res0: Map[String,Example.Job] = Map(JA -> JA does Set(T1, T3, T2), JB -> JB does Set(T2, T1), JC -> JC does Set(T1))
另一种方法是读取作业列表(创建作业作为新作业(jobID,Set.empty [Task])),然后处理任务列表包含不在作业中的条目的错误条件名单。 (每次阅读新任务时,您仍需要更新作业列表地图。)
答案 1 :(得分:2)
我确实感觉它在Scala 2.8上运行(主要是fromPath
而不是fromFile
,()
之后getLines
。它可能使用了一些Scala 2.8功能,最值得注意的是groupBy
。也可能是toSet
,但这个很容易适应2.7。
我没有测试它的文件,但是我将这些内容从val
更改为def
,并且类型签名至少匹配。
class Task(val id: String)
class Job(val id: String, val tasks: Set[Task])
// read tasks
val tasks = (
for {
line <- io.Source.fromPath("tasks.txt").getLines().toStream
tokens = line.split("\t")
jobId = tokens(0).trim
task = new Task(jobId + "." + tokens(1).trim)
} yield jobId -> task
).groupBy(_._1).map { case (key, value) => key -> value.map(_._2).toSet }
// read jobs
val jobs = Map() ++ (
for {
line <- io.Source.fromPath("jobs.txt").getLines()
job = new Job(line.trim, tasks(line.trim))
} yield job.id -> job
)
答案 2 :(得分:1)
您可以随时延迟创建对象,直到您从文件中读入所有数据,例如:
case class Task(id: String)
case class Job(id: String, tasks: Set[Task])
import scala.collection.mutable.{Map,ListBuffer}
val jobIds = Map[String, ListBuffer[String]]()
// read jobs
for (line <- io.Source.fromFile("jobs.txt").getLines) {
val job = line.trim
jobIds += (job.id -> new ListBuffer[String]())
}
// read tasks
for (line <- io.Source.fromFile("tasks.txt").getLines) {
val tokens = line.split("\t")
val job = tokens(0).trim
val task = job.id + "." + tokens(1).trim
jobIds(job) += task
}
// create objects
val jobs = jobIds.map { j =>
Job(j._1, Set() ++ j._2.map { Task(_) })
}
为了处理更多字段,您可以(通过一些努力)制作用于构建的不可变类的可变版本。然后,根据需要进行转换:
case class Task(id: String)
case class Job(val id: String, val tasks: Set[Task])
object Job {
class MutableJob {
var id: String = ""
var tasks = collection.mutable.Set[Task]()
def immutable = Job(id, Set() ++ tasks)
}
def mutable(id: String) = {
val ret = new MutableJob
ret.id = id
ret
}
}
val mutableJobs = collection.mutable.Map[String, Job.MutableJob]()
// read jobs
for (line <- io.Source.fromFile("jobs.txt").getLines) {
val job = Job.mutable(line.trim)
jobs += (job.id -> job)
}
// read tasks
for (line <- io.Source.fromFile("tasks.txt").getLines) {
val tokens = line.split("\t")
val job = jobs(tokens(0).trim)
val task = Task(job.id + "." + tokens(1).trim)
job.tasks += task
}
val jobs = for ((k,v) <- mutableJobs) yield (k, v.immutable)
答案 3 :(得分:0)
这里的一个选择是在上面MutableMap
的行上有一些可变的瞬态配置类,但是然后通过一些不可变形式传递给它你的实际班级:
val jobs: immutable.Map[String, Job] = {
val mJobs = readMutableJobs
immutable.Map(mJobs.toSeq: _*)
}
当然,您可以沿着已编码的行
实施readMutableJobs