diff options
author | Stefan Zeiger <szeiger@novocode.com> | 2016-07-05 16:08:25 +0200 |
---|---|---|
committer | Stefan Zeiger <szeiger@novocode.com> | 2016-07-07 17:34:08 +0200 |
commit | c2ca66af4214dcd2e17be038741fe63a47bb2725 (patch) | |
tree | 0bfc595953d349b519faf9b75ca3c30ac710e767 | |
parent | 6612ba010b0e70c53550d1e47141c8dc89a55f23 (diff) | |
download | scala-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.scala | 23 | ||||
-rw-r--r-- | test/junit/scala/collection/immutable/StreamTest.scala | 10 |
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)") + } } |