【问题标题】:List.distinct not removing duplicates of class with equals overrideList.distinct 不删除具有等于覆盖的类的重复项
【发布时间】:2020-01-06 02:13:58
【问题描述】:

使用下面的代码,我尝试使用 List 上的 distinct 方法删除重复元素:

class OrderDetSpecific(var size: Double,
                       var side: String,
                       var trade_id: Int,
                       var price: Double,
                       var time: String,
                       var code : String) {

  override def toString : String = {
    this.code+","+this.time+","+this.trade_id+","+this.price
  }

}


val l = List(new OrderDetSpecific(1.0 , "1" , 1 , 1.0 , "10" , "a"),new OrderDetSpecific(1.0 , "1" , 1 , 1.0 , "10" , "a"))

println(l.size)
println(l.distinct.size)

返回:

defined class OrderDetSpecific

l: List[OrderDetSpecific] = List(a,10,1,1.0, a,10,1,1.0)

2
2

但是可以看到重复的元素没有被删除。覆盖toString 方法被用作发现重复项的一部分,并且由于存在重复项,那么列表l 的大小应该是1 而不是2,因为调用l.distinct.size?

更新:

转换为案例类:

case class OrderDetSpecific(var size: Double,
                       var side: String,
                       var trade_id: Int,
                       var price: Double,
                       var time: String,
                       var code : String) {

  override def toString : String = {
    this.code+","+this.time+","+this.trade_id+","+this.price
  }

}


val l = List(new OrderDetSpecific(1.0 , "1" , 1 , 1.0 , "10" , "a"),new OrderDetSpecific(1.0 , "1" , 1 , 1.0 , "10" , "a"))

println(l.size)
println(l.distinct.size)

现在重复的元素被删除了。案例类是否在 equals 上使用值相等,从而允许 distinct 按预期运行?

当我覆盖等于时:

class OrderDetSpecific(var size: Double,
                       var side: String,
                       var trade_id: Int,
                       var price: Double,
                       var time: String,
                       var code : String) {

  override def toString : String = {
    this.code+","+this.time+","+this.trade_id+","+this.price
  }

  override def equals(that: Any): Boolean =
    that match {
      case that: OrderDetSpecific => {
        time == that.time
      }
      case _ => false
    }

}


val l = List(new OrderDetSpecific(1.0 , "1" , 1 , 1.0 , "10" , "a"),new OrderDetSpecific(1.0 , "1" , 1 , 1.0 , "10" , "a"))

println(l.size)
println(l.distinct.size)

不删除不同的元素。由于我的覆盖位于 time 属性上,该属性并不明显,不应该删除重复的元素吗?

【问题讨论】:

  • distinct 内部使用equals,对于普通类使用引用相等而不是值相等,因此所有这些元素都是不同的,无论它们具有相同的值(同时考虑到所有这些值都是可变的,它们具有相同的值只是巧合,以后可能会更改)。 - 所以它的行为符合预期。如果您愿意,您可以使所有这些值不可变并改用 case 类,或者您可以自己重写 equals(但请记住,对于可变值,引用相等 是唯一“健全”的实现方式).
  • 在列表中为 vars 的持有者添加 case 并不邪恶。但更喜欢不可变和小心使用 Maps 和 Sets。
  • @texasbruce 可能他在谈论“人类可读性”来验证输出。
  • @Luis Miguel Mejía Suárez 谢谢,请查看问题更新。为什么它是“对于可变值,引用相等是唯一的”理智的“实现”?
  • @blue-sky 具有可变值的 案例类 是代码异味。对于你自己的equals,可能你还需要覆盖canEquals & hashcode,你可以通过谷歌搜索“有效的Java自定义equals”,或者这就是为什么在Scala 我们通常不用担心,直接使用case classes。最后,对于可变值,人们可能会争辩说,只有当它们是完全相同的实例时它们才相等。 值相等对于值可以改变的东西没有意义。

标签: scala duplicates


【解决方案1】:

由于我的覆盖是按时间属性,它不应该是不同的 删除重复元素?

不,因为distinct 等效于distinctBy(identity),而distinctBy 使用HashSet,后者使用hashCode 来消除重复项,但是您没有为hashCode 提供覆盖。例如,没有hashCode 覆盖

class Foo(var x: Int) {
  override def equals(obj: Any): Boolean = true
}

val a = new Foo(42)
val b = new Foo(42)

a.## == b.##
mutable.HashSet(a, b).size == 1

输出

res0: Boolean = false
res1: Boolean = false

同时提供hashCode 覆盖

class Foo(var x: Int) {
  override def equals(obj: Any): Boolean = true
  override def hashCode(): Int = scala.runtime.Statics.anyHash(x)
}
...

我们得到

res0: Boolean = true
res1: Boolean = true

但是,没有必要摆弄这些覆盖,而是 try

l.distinctBy(_.time)

哪个输出

res0: List[OrderDetSpecific] = List(a,10,1,1.0)

【讨论】:

    猜你喜欢
    • 2012-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-19
    • 2021-12-31
    • 1970-01-01
    • 2014-02-09
    • 1970-01-01
    相关资源
    最近更新 更多