空列表平等如何工作?

How does empty List equality work?

==运算符真的是按内容比较List吗?特别是关于空列表?

以下比较按预期工作

List("A", "B", "C") == "ABC".split("").toList // true
List() == List() // true
List.empty[String] == List.empty[String] // true

然而,不同类型的空列表比较给出了令人困惑的结果:

List.empty[String] == List.empty[Int] // true: on different types?

编辑:在最初的问题中,我做了一个误导性的测试用例,安德烈已经澄清了这一点。谢谢。转载于此

val emptyStrSplit = "".split("").toList // List("") and not List() as displayed in Console
List.empty[String] == emptyStrSplit // false: b/c List() != List("")
  • List.empty[String] 是单例对象 Nil,它扩展了 List[Nothing](协变,List[String] 的子类型)。
  • List.empty[Int] 是单例对象 Nil,它扩展了 List[Nothing](协变,List[Int] 的子类型)。
  • 每个单例对象都等于它自己。
  • 因此,Nil == Nil 为真。

因此,从本质上讲,您有一个对象 Nil,它同时属于 List[String] 类型和 List[Int] 类型。如果你有子类型,这就是你得到的。我在这里没有看到任何奇怪或矛盾的地方。

如果要确保类型相同,可以使用implicit类型A =:= B的证据,默认值为null,然后检查是否非null证据由编译器提供:

def eqSameType[A, B](a: A, b: B)(implicit ev: A =:= B = null) = 
  if (ev == null) false else a == b

示例:

scala> eqSameType(List.empty[Int], List.empty[String])
res4: Boolean = false

添加到 Andrey 的回答中,即使 List.empty[T](或 List[T]())做了 return List 的新实例,您仍然应该期待各种类型的空列表由于类型擦除而相等。例如,以 ListBuffer 为例,其 empty 方法每次都会 return 一个新的 ListBuffer

import scala.collection.mutable.ListBuffer
ListBuffer.empty[Int] == ListBuffer.empty[String]

如果你想要一种方法来检测两个列表何时具有不同的编译时类型,你可以使用 TypeTags:

import scala.reflect.runtime.universe.{ TypeTag, typeTag }

def equalAndSameType[A: TypeTag, B: TypeTag](as: Seq[A], bs: Seq[B]) =
  typeTag[A] == typeTag[B] && as == bs

equalAndSameType(List.empty[Int], List.empty[String]) // returns false

不过我不确定这什么时候有用。

编辑:这个使用 ClassTag 的实现不够好。 Brian 使用 TypeTag 的答案更好。

尽管从 Scala 的角度来看,Andrey 的回答非常合理。我觉得让 List.empty[String] == List.empty[Int] 为假而不是真在商业上更有意义。下面是一个使用上下文绑定来支持以下内容的自定义比较器。不确定这是否是最优雅的方式。

import scala.reflect.{ClassTag, classTag}

def customCompareLists[T1: ClassTag, T2: ClassTag](l1: List[T1], l2: List[T2]): Boolean = {
  classTag[T1] == classTag[T2] && l1 == l2
}

customCompareLists(List(), List()) // true
customCompareLists(List.empty[Double], List.empty[Double]) // true
customCompareLists(List.empty[String], List.empty[Int]) // false
customCompareLists(List(1,2), List("A")) // false
customCompareLists(List(1,2), List(1,2)) // true

// FAILED on this case
customCompareLists(List.empty[List[String]], List.empty[List[Int]]) // true: INCORRECT

即使忽略类型擦除问题和“它们实际上是同一个对象”问题,equalsSeq 上的文档指出:

def equals(that: Any): Boolean

The equals method for arbitrary sequences.

returns: true if that is a sequence that has the same elements as this sequence in the same order, false otherwise

这使得所有空序列相等:它们具有相同顺序的相同元素,即none。空 List 等于空 VectorQueueStream 等等等等

您可能还对提供类型安全相等性的库感兴趣,例如Scalactic, Scalaz