Scala中自定义类作为Map键时contains判断失效的解决方案
解决Scala中自定义类作为Map键的相等性问题
嘿,这个问题太典型了!你猜的完全没错——Scala里确实需要处理equals和hashCode方法来让Map正确识别相同属性的自定义类实例,而且有两种常用的方式,比C#还灵活呢:
1. 优先使用Case Class(最推荐!)
Scala的case class会自动帮你生成基于所有构造参数的equals和hashCode方法,还附带toString、copy等实用功能,完全不用自己写冗余代码。
只需要把你的Song类改成case class就行:
// 把class改成case class,构造参数默认是val(不可变),正好符合Map键的要求 case class Song(title: String, singer: String, duration: Int) // 按需调整你的参数 // 原来的Map和方法完全不用改 var listeningCountMap = Map[Song, Int]() def updateSongCount(song : Song) { if(this.listeningCountMap.contains(song)) { this.listeningCountMap(song) += 1 } else { this.listeningCountMap += (song -> 1) } }
这样一来,只要两个Song实例的所有构造参数都相同,Map就会把它们当成同一个键,完美解决你的问题。
2. 手动重写equals和hashCode(特殊场景用)
如果因为某些原因不能用case class(比如需要继承其他非case类,或者有自定义的相等逻辑),那就要手动重写这两个方法,注意几个关键点:
equals要先判断类型,再比较所有用来定义“相等”的属性hashCode必须基于和equals相同的属性生成,否则会导致Map行为异常- 尽量用不可变属性(
val),如果用var的话,属性修改后会改变hashCode,导致Map里的键找不到
举个手动实现的例子:
class Song(val title: String, val singer: String) { // 用val保证属性不可变 // 重写equals:用模式匹配判断类型,再比较属性 override def equals(obj: Any): Boolean = obj match { case that: Song => this.title == that.title && this.singer == that.singer case _ => false } // 重写hashCode:基于参与相等判断的属性生成哈希值 override def hashCode(): Int = { // 用Scala内置的MurmurHash3来组合哈希值,比自己拼接更可靠 import scala.util.hashing.MurmurHash3._ val baseSeed = 0 val titleHash = stringHash(title, baseSeed) stringHash(singer, titleHash) } }
小提醒
不管用哪种方式,都要保证用来判断相等的属性是不可变的(用val)。如果用var修改了属性值,对应的hashCode也会改变,这时候Map里原来的键就会“失踪”,再也找不到了哦!
内容的提问来源于stack exchange,提问作者Shirli
相关产品推荐
相关产品推荐

