惜朝在来往的扎堆里问:
scala> List(1, 1, 2) == Seq(1, 1, 2)
res219: Boolean = true
scala里Seq和List是一会儿事?
这个问题归根到底在于 ==
在集合里是怎么实现的?在scala里==
的语义等同于java里的equals
,我们跟踪一下
val a = List(1,2,3)
val b = Seq(1,2,3)
a.equals(b) // 设置断点
注意,在a.equals(b)
出设置断点,scala-ide不一定能进入内部逻辑,你还是需要在它父类equals
方法内设置断点才行。
上面的equals实际会到 GenSeqLike.equals
,见下图:
它的逻辑是判断两个集合是否“可比较”(canEqual),如果可比较,则判断内部的元素是否相同:
override def equals(that: Any): Boolean = that match {
case that: GenSeq[_] => (that canEqual this) && (this sameElements that)
case _ => false
}
于是我们推测 List(1,2,3)
与 Seq(1,2,3)
的容器类型应该是相同的类型或有继承关系,但是进入到canEqual
逻辑内部,无法验证这个判断,它直接返回true,按理说应该对两个容器的类型进行比较一下才合适(看来只是给用户实现的集合类型实现equals时留了一个扩展点,scala自己的集合类型并不做类型判断)。
接下来进入 sameElements
逻辑,因为List
混入了LinearSeqOptimized
特质,这块的逻辑是在LinearSeqOptimized
中的,见下图:
我们看到它通过模式匹配,要求目标集合也必须是LinearSeq
类型。然后迭代并比较了两个容器内的各个元素是否相同,都相同的话就认为两个容器也相同。不过从这里的逻辑我们也可以判断出来,两个容器equals
为true的话,并不一定需要是完全同样的类型或者有父子关系。我们验证一下:
1)两个集合分别是Seq特质与Set特质下的子类,是两种不一样的集合
scala> List(1,2,3) == Set(1,2,3)
res28: Boolean = false
2) 两个集合都是Set特质下的子类
scala> HashSet(1,2,3) == TreeSet(1,2,3)
res29: Boolean = true
3) 两个集合都是Seq特质下的子类
scala> ListBuffer(1,2) == LinkedList(1,2)
res20: Boolean = true
4) 两个集合都是Seq特质下的子类,不过Queue
是LinearSeq
下的,而Range
是IndexedSeq
下的
scala> Queue(1,2) == Range(1,2)
res18: Boolean = false
5) 两个集合都是Seq特质下的子类,Seq(1,2,3)
的实现是?
scala> Range(1,2,3) == Seq(1,2,3)
res12: Boolean = false
第1种情况好理解,List
与 Set
毕竟是另种含义不同集合,Set
的实现也不会是LinearSeq
特质的,所以返回false.第2种也容易理解,两个集合都是Set
特质下的。
问题是3,4,5,为何Seq
下会有多种情况,这还要我们再全面的看一下scala的集合框架,借用
这里的图片:
正是因为 Seq
特质下,又分为了IndexedSeq
和 LinearSeq
两个分支,并且这两个特质中各自对 sameElements
的逻辑有不同的实现,使得IndexedSeq
的集合与LinearSeq
下的集合比较时不可能相等。
另,对于 List(1,2,3)
或 Seq(1,2,3)
在构造集合的背后逻辑,可以参考这篇:通过List.apply方法构造List的背后逻辑
第4个例子中,Range(1, 2) 最后的值是Range(1) ,肯定不会和Queue(1, 2)
scala> Range(1, 3) == Queue(1, 2)
res0 : Boolean = true
第5个例子中,Range (1, 2, 3) 与 Vector (1, 2, 3) 肯定不会相等,它们的值不一样,
scala> Range(1, 4) == Vector(1, 2, 3)
res1 : Boolean = true
我觉得这样写更能表示同一个Seq特质下的子类相等比较。
以上个人建议。但还是向楼主学到了许多。
多谢指出。4和5的例子确实不正确。
今天看到这篇blog,也可以参考:http://blog.bruchez.name/2013/05/scala-array-comparison-without-phd.html