使用一种方法在Scala中初始化一些变量

时间:2013-07-01 07:52:14

标签: scala

以下是我从Java转换的代码的一部分

class Regex(valCollection: Collection[MyClass3]) {
  private val val1 = new HashMap[String, MyClass1]
  private val val2 = new HashMap[String, String]
  private val val3 = new HashMap[String, MyClass2]

  private val filteredValCollection = valCollection.map(x => {
    val valCollectionItem = getValCollectionItem(x)

    x.key match {
      case "123" => val1 + (valCollectionItem -> MyClass1.parse(x.value)) //TODO -- append to val1
      case "456" => val2 + (valCollectionItem -> x.value) //TODO -- append to val2
      case "789" => val3 + (valCollectionItem -> MyClass2.parse(x.value)) //TODO -- append to val3
    }

    valCollectionItem
  })

  def getValCollectionItem = { /*.....*/}
}

1)我想要做的只是使用不可变集合和不变性初始化所有4个集合:val1, val2, val3 and filteredValCollection。正如您所看到的,filteredValCollection已初始化,并且没有问题。但是,

case "123" => val1 + (valCollectionItem -> MyClass1.parse(x.value))

将结果返回到val2val3

我的想法是,我必须从tuple返回valCollection.map并初始化我想要的所有收藏品。

那我该怎么做?

2)由于此代码来自Java代码,Scala世界中是否有更高效的Collection[MyClass3]模拟?

2 个答案:

答案 0 :(得分:2)

使用不可变的集合

def parseValCollection = {
  val elemAndItems = valCollection.map{ x => x -> getValCollectionItem(x) }
  val valCollectionItems = elemAndItems.map{ case (_, item) -> item }
  val val1Map = elemAndItems.filter{ case (x, _) => x.key == "123" }.
    map{ case (x, item) => item -> MyClass1.parse(x.value) }.toMap
  val val2Map = elemAndItems.filter{ case (x, _) => x.key == "456" }.
    map{ case (x, item) => item -> x.value }.toMap
  val val3Map = elemAndItems.filter{ case (x, _) => x.key == "789" }.
    map{ case (x, item) => item -> MyClass2.parse(x.value) }.toMap

  (valCollectionItems, val1Map, val2Map, val3Map)
}

private val (valCollectionItems, val1Map, val2Map, val3Map) = parseValCollection

如果您只想在使用valCollection后迭代foldLeft,但您不应该正常进行 - 这是过早优化的经典案例。

如果您想减少迭代次数,可以将filter{...}.map{...}.toMap替换为collect{...}(breakOut)

val val1Map: Map[String, MyClass1] = elemAndItems.collect{
    case (x, item) if x.key == "123" => item -> MyClass1.parse(x.value)
  }(breakOut)

或者,您可以在view之前添加filter以使用延迟收藏。

使用可变集合

但是如果你想将代码从java转换为scala,你可以使用可变集合:

import collection.{mutable => m}

class Regex(valCollection: Iterable[MyClass3]) {
  private val val1 = m.Map[String, MyClass1]()
  private val val2 = m.Map[String, String]()
  private val val3 = m.Map[String, MyClass2]()

  private val filteredValCollection = m.Seq[ItemType]()

  for( x <- valCollection){
    val valCollectionItem = getValCollectionItem(x)
    filteredValCollection += valCollectionItem
    x.key match {
      case "123" => val1 += (valCollectionItem -> MyClass1.parse(x.value))
      case "456" => val2 += (valCollectionItem -> x.value)
      case "789" => val3 += (valCollectionItem -> MyClass2.parse(x.value))
    }
  }

  def getValCollectionItem: ItemType = { /*.....*/}
}

更高效的Collection [MyClass3]模拟

如果你想从scala代码调用这个构造函数,你应该使用所有scala collectionsTraversableIterable的公共接口,并给调用者留下实现的选择。

答案 1 :(得分:0)

您想要的返回类型到底是什么? val1val2val3?我真的不清楚你要做什么。我假设您要将valCollection分为三个HashMap

让我们为此制作一个案例类:

case class CollectionResult(
  val val1: HashMap[String, MyClass1],
  val val2: HashMap[String, String],
  val val3: HashMap[String, MyClass2]
)

如果所有3个结果都具有相同的类型,则可以使用简单的Tuple3 ...

但是让我们使用CollectionResult。您现在需要一个带Collection[MyClass3]并返回CollectionResult

的函数
def partition(input: Collection[MyClass3]): CollectionResult

顾名思义,我们会经历input,在我们去的时候对值进行分区。由于我们需要一个CollectionResult,其中很多,我们将使用foldLeft

def partition(input: Collection[MyClass3]): CollectionResult = {
  val initialAccumulator = CollectionResult(HashMap.empty, HashMap.empty, HashMap.empty, Collection.empty)
  input.foldLeft(initialAccumulator)((accumulator, inputVal) => {
    val newVal1 = (if (inputVal.x == "123")  accumulator.val1 + (inputVal -> MyClass1.parse(inputVal.value)) else accumulator.val1)
    val newVal2 = (if (inputVal.x == "456") accumulator.val2 + inputVal.value else accumulator.val2)
    val newVal3 = (if (inputVal.x == "789")  accumulator.val3 + (inputVal -> MyClass2.parse(inputVal.value)) else accumulator.val3)

    CollectionResult(newVal1, newVal2, newVal3)
  })
}

正如您所看到的,CollectionResult的实例用于通过foldLeft()进程不可避免地传递状态。

但等等,这似乎是一个普遍的过程。如果Scala有一个内置的方法来做这件事会不会很好?它确实:groupBy()。它只需要一个带入值的函数,并返回结果Map的键。

让我们用它:

val partitioned: Map[String, Class3] = valCollection.groupBy(inputVal => inputVal.x)
val transformed: Map[String, Any] = partitioned.map((keyValue) => {
  val (key, value) = keyValue
  key match {
    case "123" => MyClass1.parse(value)
    case "789" => MyClass2.parse(value)
    case _     => value
  }
})

这非常简洁,但它的缺点是需要多次迭代(可能不是真正的问题)并且必须使用Any类型(更大的问题)。