scala
package caseclass
import scala.collection.mutable
object caseclass1 {
class Book(var id: Int, var name: String) {
override def equals(obj: Any): Boolean = {
val other = obj.asInstanceOf[Book]
other.id == id && other.name == name
}
override def hashCode(): Int = id
}
def main(args: Array[String]): Unit = {
val set1: mutable.Set[Int] = mutable.Set(1, 2)
set1 += 1
set1 += 1
set1 += 1
set1 += 1
println(set1)
val book1 = new Book(1, "西游记")
val book2 = new Book(1, "西游记")
println(book1 == book2)
//定义一个set,它的类型是Book,它可以装入的数据是book类型
val set2: mutable.Set[Book] = mutable.Set()
set2 += book1
set2 += book2
println(set2)
}
}
这段 Scala 代码主要演示了可变集合mutable.Set的特性,以及自定义类中equals和hashCode方法对集合去重逻辑的影响,具体分析如下:
代码功能与执行结果
- 整数集合去重 :定义
mutable.Set[Int]类型的set1,初始包含1和2,多次添加1后,集合仍为Set(1, 2)。因为Set的特性是自动去重,整数类型的相等性判断基于值,重复元素会被自动忽略。 - 自定义类对象的相等性 :自定义
Book类重写了equals方法(通过id和name判断相等)和hashCode方法(基于id生成哈希值)。创建book1和book2(id和name均相同),book1 == book2输出true,符合重写...
解释
这段 Scala 代码主要围绕可变集合mutable.Set的去重逻辑 、自定义类的equals和hashCode方法重写展开,我们逐部分拆解解释:
1. 整数集合的去重演示
scala
arduino
val set1: mutable.Set[Int] = mutable.Set(1, 2)
set1 += 1
set1 += 1
println(set1) // 输出:Set(1, 2)
Set(无论可变 / 不可变)的核心特性是元素唯一性,会自动过滤重复元素。- 这里多次向
set1添加1,但最终集合仍只保留一个1,因为整数类型(Int)的相等性判断是基于值本身 的,1 == 1始终成立,所以重复添加无效。
2. 自定义类Book的equals和hashCode重写
scala
kotlin
class Book(var id: Int, var name: String) {
// 重写equals:通过id和name判断两个Book对象是否相等
override def equals(obj: Any): Boolean = {
val other = obj.asInstanceOf[Book] // 将Any类型强转为Book
other.id == id && other.name == name
}
// 重写hashCode:基于id生成哈希值
override def hashCode(): Int = id
}
equals方法 :默认情况下,类的equals是基于对象引用 的(即两个对象必须是内存中同一个实例才相等)。这里重写后,只要两个Book对象的id和name相同,就判定为相等(book1 == book2返回true)。hashCode方法 :哈希值是集合(如Set)存储元素时的 "快速索引",Set会先通过hashCode分组,再对同组元素用equals判断是否重复。这里hashCode基于id生成,意味着id相同的Book对象会被分到同一组。
3. Book对象存入Set的 "看似矛盾" 现象
scala
ini
val book1 = new Book(1, "西游记")
val book2 = new Book(1, "西游记")
println(book1 == book2) // 输出:true
val set2: mutable.Set[Book] = mutable.Set()
set2 += book1
set2 += book2
println(set2) // 输出:Set(Book@1, Book@1)(看似重复,实际是两个不同实例)
-
book1 == book2为true:因为我们重写了equals,book1和book2的id、name都相同,所以相等性判断成立。 -
set2中同时存在book1和book2:这是关键的 "矛盾点"------ 明明book1 == book2为true,但Set却没有去重?原因在于:Set的去重逻辑依赖hashCode和equals的协同 ,且对于普通类 (非样例类),即使重写了equals和hashCode,Set仍会结合对象引用辅助判断(或因 JVM 的哈希碰撞处理细节)。更本质的是:book1和book2是两个不同的对象实例 (内存地址不同),而mutable.Set对普通类的去重逻辑,在hashCode相同、equals为true时,仍可能因 "实例唯一性" 保留多个对象(这是普通类与样例类的核心区别)。
4. 为什么用样例类(case class)就能正确去重?
如果将Book定义为样例类:
scala
javascript
case class Book(var id: Int, var name: String) // 无需手动重写equals/hashCode
此时set2 += book1; set2 += book2会输出Set(Book(1,西游记)),因为样例类会自动生成基于构造参数的equals和hashCode ,完全遵循 "值相等即对象相等" 的逻辑,Set会正确识别重复并去重。
总结
Set的去重依赖hashCode(分组)和equals(判重)的协同。- 普通类需手动重写
equals和hashCode,但仍可能因 "实例引用" 导致Set去重失效;样例类自动实现正确的equals和hashCode,是 Scala 中实现 "值相等" 的推荐方式。 - 整数等基础类型的
equals和hashCode已默认实现 "值相等",因此Set对基础类型的去重始终有效。