1

这是我从 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))

将结果返回到无处以及 forval2val3

我的想法是,我必须返回一个tuplefromvalCollection.map并在之后初始化我想要的所有集合。

那么我该怎么做呢?

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

4

2 回答 2

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{...}.toMapcollect{...}(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 集合的公共接口:Traversable或者Iterable让调用者选择实现。

于 2013-07-01T08:31:31.117 回答
0

你想要的返回类型到底是什么?val1, val2, val3? 我真的不清楚你想做什么。我将假设您要划分valCollection为三个HashMaps。

让我们为此创建一个案例类:

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

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

但是让我们一起工作CollectionResult。您现在需要一个接受 aCollection[MyClass3]并返回 a的函数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类型(一个更大的问题)。

于 2013-07-01T08:35:00.310 回答