summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Zeiger <szeiger@novocode.com>2016-07-05 16:08:25 +0200
committerStefan Zeiger <szeiger@novocode.com>2016-07-07 17:34:08 +0200
commitc2ca66af4214dcd2e17be038741fe63a47bb2725 (patch)
tree0bfc595953d349b519faf9b75ca3c30ac710e767
parent6612ba010b0e70c53550d1e47141c8dc89a55f23 (diff)
downloadscala-c2ca66af4214dcd2e17be038741fe63a47bb2725.tar.gz
scala-c2ca66af4214dcd2e17be038741fe63a47bb2725.tar.bz2
scala-c2ca66af4214dcd2e17be038741fe63a47bb2725.zip
SI-6881 Detect reference equality when comparing streams
`==` already covers this case. We override `equals` in `Stream` to do the same when `equals` is called directly. This takes care of identical streams. To support short-circuiting equality checks on elements prepended to identical streams we also override `sameElements` in `Cons` to treat the case where both sides are `Cons` separately. Tests in StreamTest.test_reference_equality.
-rw-r--r--src/library/scala/collection/immutable/Stream.scala23
-rw-r--r--test/junit/scala/collection/immutable/StreamTest.scala10
2 files changed, 33 insertions, 0 deletions
diff --git a/src/library/scala/collection/immutable/Stream.scala b/src/library/scala/collection/immutable/Stream.scala
index d135bb29a8..db19df315f 100644
--- a/src/library/scala/collection/immutable/Stream.scala
+++ b/src/library/scala/collection/immutable/Stream.scala
@@ -1029,6 +1029,8 @@ sealed abstract class Stream[+A] extends AbstractSeq[A]
*/
override def stringPrefix = "Stream"
+ override def equals(that: Any): Boolean =
+ if (this eq that.asInstanceOf[AnyRef]) true else super.equals(that)
}
/** A specialized, extra-lazy implementation of a stream iterator, so it can
@@ -1171,6 +1173,27 @@ object Stream extends SeqFactory[Stream] {
tlVal
}
+
+ override /*LinearSeqOptimized*/
+ def sameElements[B >: A](that: GenIterable[B]): Boolean = {
+ @tailrec def consEq(a: Cons[_], b: Cons[_]): Boolean = {
+ if (a.head != b.head) false
+ else {
+ a.tail match {
+ case at: Cons[_] =>
+ b.tail match {
+ case bt: Cons[_] => (at eq bt) || consEq(at, bt)
+ case _ => false
+ }
+ case _ => b.tail.isEmpty
+ }
+ }
+ }
+ that match {
+ case that: Cons[_] => consEq(this, that)
+ case _ => super.sameElements(that)
+ }
+ }
}
/** An infinite stream that repeatedly applies a given function to a start value.
diff --git a/test/junit/scala/collection/immutable/StreamTest.scala b/test/junit/scala/collection/immutable/StreamTest.scala
index fad4e502eb..7046525f37 100644
--- a/test/junit/scala/collection/immutable/StreamTest.scala
+++ b/test/junit/scala/collection/immutable/StreamTest.scala
@@ -107,4 +107,14 @@ class StreamTest {
def withFilter_map_properly_lazy_in_tail: Unit = {
assertStreamOpLazyInTail(_.withFilter(_ % 2 == 0).map(identity), List(1, 2))
}
+
+ @Test // SI-6881
+ def test_reference_equality: Unit = {
+ // Make sure we're tested with reference equality
+ val s = Stream.from(0)
+ assert(s == s, "Referentially identical streams should be equal (==)")
+ assert(s equals s, "Referentially identical streams should be equal (equals)")
+ assert((0 #:: 1 #:: s) == (0 #:: 1 #:: s), "Cons of referentially identical streams should be equal (==)")
+ assert((0 #:: 1 #:: s) equals (0 #:: 1 #:: s), "Cons of referentially identical streams should be equal (equals)")
+ }
}