空列表平等如何工作?
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
即使忽略类型擦除问题和“它们实际上是同一个对象”问题,equals
在 Seq
上的文档指出:
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
等于空 Vector
、Queue
、Stream
等等等等
==
运算符真的是按内容比较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
即使忽略类型擦除问题和“它们实际上是同一个对象”问题,equals
在 Seq
上的文档指出:
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
等于空 Vector
、Queue
、Stream
等等等等