使用不可变集合

时间:2015-11-02 21:08:35

标签: scala

我正在学习Scala,我不得不说我真的很喜欢它。 我将Scala移植到我之前的Java项目中,试图在我能够/知道如何(主要是模式匹配和尝试使用val,而不是递归或函数)时利用该语言。

在java项目中,我有一个HashMap,它开始为空,并且会通过使用某些方法来填充。然后,当然,我使用hmap.get(key)来检索对象。 我这次想使用不可变HashMap,但我有点挣扎!这是我的代码:

val recipes = new HashMap[String,Any]()

private def addCrucibleRecipe(tag: String, tagAddon: String, result: ItemStack, cat: Any, aspects: AspectList) { 
  val recipe = new CrucibleRecipe(some params)
  if(recipe.isInstanceOf[CrucibleRecipe]) recipes + (tag -> recipe) 
  else throw new IllegalArgumentException(tag + " is not a valid recipe!")
}

正如您所看到的,我正在使用+运算符,该运算符应返回一个包含先前值的新HashMap和尾部的新值recipes.get(key)。 但是,当我拨打None时,该列表包含选项HashMap。 这意味着我仍然指向旧的{{1}}。

我该如何更新参考?也许这可能听起来很愚蠢,但我对函数式编程来说真的很陌生。它比一个可变的更好吗?

如果在Scala中有更多功能或更有效的方法,请告诉我,我渴望学习:)

1 个答案:

答案 0 :(得分:1)

是的,+方法将返回一个新的更新的地图实例。但您在此处引用可变值 recipes 。不幸的是,这个可变映射继承了用于不可变映射的方法,比如+,所以你创建了一个新的(可变的但不同的)映射,并且#34;把它扔掉了#34;。这就是我的意思:

import scala.collection.mutable

val m0 = mutable.Map.empty[String, Int]
m0.put("hello", 1)
val m1 = m0 + ("world" -> 2)
m0.keys // Set(hello) -- this is the "old" map still!
m1.keys // Set(world, hello) -- this is the newly created map

所以正确的方法是使用不可变的地图和var来存储它:

var m2 = Map.empty[String, Int]  // immutable map
m2 = m2 + ("hello" -> 1)
m2 +=     ("world" -> 2)  // were m2 += ... is short for m2 = m2 + ...

当然,现在您已将可变状态移动到具有变量m2 (或示例中的var recipes)。所以你必须定义"断点"您希望在程序中存储状态的位置。例如,您可以向上移动一级:

case class Recipe(ingredients: String*)

case class Recipes(map: Map[String, Recipe]) {
  def add(name: String, recipe: Recipe): Recipes =
    new Recipes(map + (name -> recipe))
}

val r0 = Recipes(Map.empty)
val r1 = r0.add("pancakes", Recipe("milk", "eggs", "farine"))

请注意,在您的示例addCrucibleRecipe中没有返回值,因此您似乎打算覆盖包含对象中的某个状态。在这里的case-class示例中,我将返回该对象的全新副本。

您可能想看看这些问题: