Skip to content

Commit 7db02fe

Browse files
authored
Merge pull request scala#10425 from RustedBones/equals-converters
Define equality between converter wrappers
2 parents 0f5746f + 0b0478a commit 7db02fe

File tree

2 files changed

+165
-47
lines changed

2 files changed

+165
-47
lines changed

src/library/scala/collection/convert/JavaCollectionWrappers.scala

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,33 @@ private[collection] object JavaCollectionWrappers extends Serializable {
3434
def hasMoreElements = underlying.hasNext
3535
def nextElement() = underlying.next()
3636
override def remove() = throw new UnsupportedOperationException
37+
override def equals(other: Any): Boolean = other match {
38+
case that: IteratorWrapper[_] => this.underlying == that.underlying
39+
case _ => false
40+
}
41+
override def hashCode: Int = underlying.hashCode()
3742
}
3843

3944
@SerialVersionUID(3L)
4045
class JIteratorWrapper[A](val underlying: ju.Iterator[A]) extends AbstractIterator[A] with Iterator[A] with Serializable {
4146
def hasNext = underlying.hasNext
4247
def next() = underlying.next
48+
override def equals(other: Any): Boolean = other match {
49+
case that: JIteratorWrapper[_] => this.underlying == that.underlying
50+
case _ => false
51+
}
52+
override def hashCode: Int = underlying.hashCode()
4353
}
4454

4555
@SerialVersionUID(3L)
4656
class JEnumerationWrapper[A](val underlying: ju.Enumeration[A]) extends AbstractIterator[A] with Iterator[A] with Serializable {
4757
def hasNext = underlying.hasMoreElements
4858
def next() = underlying.nextElement
59+
override def equals(other: Any): Boolean = other match {
60+
case that: JEnumerationWrapper[_] => this.underlying == that.underlying
61+
case _ => false
62+
}
63+
override def hashCode: Int = underlying.hashCode()
4964
}
5065

5166
trait IterableWrapperTrait[A] extends ju.AbstractCollection[A] {
@@ -57,13 +72,11 @@ private[collection] object JavaCollectionWrappers extends Serializable {
5772

5873
@SerialVersionUID(3L)
5974
class IterableWrapper[A](val underlying: Iterable[A]) extends ju.AbstractCollection[A] with IterableWrapperTrait[A] with Serializable {
60-
import scala.runtime.Statics._
61-
override def equals(other: Any): Boolean =
62-
other match {
63-
case other: IterableWrapper[_] => underlying.equals(other.underlying)
64-
case _ => false
65-
}
66-
override def hashCode = finalizeHash(mix(mix(0xcafebabe, "IterableWrapper".hashCode), anyHash(underlying)), 1)
75+
override def equals(other: Any): Boolean = other match {
76+
case that: IterableWrapper[_] => this.underlying == that.underlying
77+
case _ => false
78+
}
79+
override def hashCode: Int = underlying.hashCode()
6780
}
6881

6982
@SerialVersionUID(3L)
@@ -74,6 +87,11 @@ private[collection] object JavaCollectionWrappers extends Serializable {
7487
def iterator = underlying.iterator.asScala
7588
override def iterableFactory = mutable.ArrayBuffer
7689
override def isEmpty: Boolean = !underlying.iterator().hasNext
90+
override def equals(other: Any): Boolean = other match {
91+
case that: JIterableWrapper[_] => this.underlying == that.underlying
92+
case _ => false
93+
}
94+
override def hashCode: Int = underlying.hashCode()
7795
}
7896

7997
@SerialVersionUID(3L)
@@ -86,6 +104,11 @@ private[collection] object JavaCollectionWrappers extends Serializable {
86104
override def knownSize: Int = if (underlying.isEmpty) 0 else super.knownSize
87105
override def isEmpty = underlying.isEmpty
88106
override def iterableFactory = mutable.ArrayBuffer
107+
override def equals(other: Any): Boolean = other match {
108+
case that: JCollectionWrapper[_] => this.underlying == that.underlying
109+
case _ => false
110+
}
111+
override def hashCode: Int = underlying.hashCode()
89112
}
90113

91114
@SerialVersionUID(3L)
@@ -254,7 +277,7 @@ private[collection] object JavaCollectionWrappers extends Serializable {
254277
def getKey = k
255278
def getValue = v
256279
def setValue(v1 : V) = self.put(k, v1)
257-
280+
258281
// It's important that this implementation conform to the contract
259282
// specified in the javadocs of java.util.Map.Entry.hashCode
260283
//
@@ -529,6 +552,13 @@ private[collection] object JavaCollectionWrappers extends Serializable {
529552
} catch {
530553
case ex: ClassCastException => null.asInstanceOf[V]
531554
}
555+
556+
override def equals(other: Any): Boolean = other match {
557+
case that: DictionaryWrapper[_, _] => this.underlying == that.underlying
558+
case _ => false
559+
}
560+
561+
override def hashCode: Int = underlying.hashCode()
532562
}
533563

534564
@SerialVersionUID(3L)

test/junit/scala/collection/convert/EqualsTest.scala

Lines changed: 127 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,23 @@ package scala.collection.convert
44
import org.junit.Test
55
import org.junit.Assert._
66

7+
import java.util.{
8+
AbstractList,
9+
AbstractMap,
10+
AbstractSet,
11+
Collections,
12+
Collection => JCollection,
13+
HashSet => JHashSet,
14+
List => JList,
15+
Map => JMap,
16+
Set => JSet
17+
}
18+
import java.lang.{Iterable => JIterable}
19+
import java.util.concurrent.{ConcurrentHashMap => JCMap}
20+
import scala.collection.{AbstractIterable, concurrent, mutable}
721
import scala.jdk.CollectionConverters._
822
import JavaCollectionWrappers._
923

10-
import java.util.{AbstractList, AbstractSet, List => JList, Set => JSet}
11-
1224
class JTestList(vs: Int*) extends AbstractList[Int] {
1325
def this() = this(Nil: _*)
1426
override def size = vs.size
@@ -21,58 +33,134 @@ class JTestSet(vs: Int*) extends AbstractSet[Int] {
2133
override def iterator = vs.iterator.asJava
2234
}
2335

36+
object JTestMap {
37+
case class JTestMapEntry(key: Int, value: String) extends JMap.Entry[Int, String] {
38+
override def getKey: Int = key
39+
override def getValue: String = value
40+
override def setValue(value: String): String =
41+
throw new UnsupportedOperationException("Cannot set value on JTestMapEntry")
42+
}
43+
}
44+
45+
class JTestMap(vs: (Int, String)*) extends AbstractMap[Int, String] {
46+
import JTestMap._
47+
override def entrySet(): JSet[JMap.Entry[Int, String]] = {
48+
val entrySet = new JHashSet[JMap.Entry[Int, String]](vs.size);
49+
vs.foreach { case (k, v) => entrySet.add(JTestMapEntry(k, v)) }
50+
entrySet
51+
}
52+
}
53+
2454
/** Test that collection wrappers forward equals and hashCode where appropriate. */
2555
class EqualsTest {
2656

27-
def jlstOf(vs: Int*): JList[Int] = new JTestList(vs: _*)
28-
def jsetOf(vs: Int*): JSet[Int] = new JTestSet(vs: _*)
29-
30-
// Seq extending AbstractList inherits equals
57+
def jListOf(vs: Int*): JList[Int] = new JTestList(vs: _*)
58+
def jSetOf(vs: Int*): JSet[Int] = new JTestSet(vs: _*)
59+
def jMapOf(vs: (Int, String)*): JMap[Int, String] = new JTestMap(vs: _*)
3160

32-
@Test def `List as JList has equals`: Unit = {
33-
val list = List(1, 2, 3)
34-
val jlst = new SeqWrapper(list)
35-
assertEquals(jlstOf(1, 2, 3), jlst)
36-
assertEquals(jlst, jlstOf(1, 2, 3))
37-
assertTrue(jlst == jlstOf(1, 2, 3))
38-
assertEquals(jlst.hashCode, jlst.hashCode)
61+
// SeqWrapper extending util.AbstractList inherits equals
62+
@Test def `Seq as JList has equals`: Unit = {
63+
def seq = Seq(1, 2, 3)
64+
def jList = new SeqWrapper(seq)
65+
assertEquals(jList, jList)
66+
assertEquals(jListOf(1, 2, 3), jList)
67+
assertEquals(jList, jListOf(1, 2, 3))
68+
assertTrue(jList == jListOf(1, 2, 3))
69+
assertEquals(jList.hashCode, jList.hashCode)
3970
}
4071

72+
// SetWrapper extending util.AbstractSet inherits equals
4173
@Test def `Set as JSet has equals`: Unit = {
42-
val set = Set(1, 2, 3)
43-
val jset = new SetWrapper(set)
44-
assertEquals(jsetOf(1, 2, 3), jset)
45-
assertEquals(jset, jsetOf(1, 2, 3))
46-
assertTrue(jset == jsetOf(1, 2, 3))
47-
assertEquals(jset.hashCode, jset.hashCode)
74+
def set = Set(1, 2, 3)
75+
def jSet = new SetWrapper(set)
76+
assertEquals(jSet, jSet)
77+
assertEquals(jSetOf(1, 2, 3), jSet)
78+
assertEquals(jSet, jSetOf(1, 2, 3))
79+
assertTrue(jSet == jSetOf(1, 2, 3))
80+
assertEquals(jSet.hashCode, jSet.hashCode)
4881
}
4982

83+
// MapWrapper extending util.AbstractMap inherits equals
5084
@Test def `Map as JMap has equals`: Unit = {
51-
val map = Map(1 -> "one", 2 -> "two", 3 -> "three")
52-
val jmap = new MapWrapper(map)
53-
assertEquals(jmap, jmap)
85+
def map = Map(1 -> "one", 2 -> "two", 3 -> "three")
86+
def jMap = new MapWrapper(map)
87+
assertEquals(jMap, jMap)
88+
assertEquals(jMapOf(1 -> "one", 2 -> "two", 3 -> "three"), jMap)
89+
assertEquals(jMap, jMapOf(1 -> "one", 2 -> "two", 3 -> "three"))
90+
assertTrue(jMap == jMapOf(1 -> "one", 2 -> "two", 3 -> "three"))
91+
assertEquals(jMap.hashCode, jMap.hashCode)
5492
}
5593

56-
@Test def `Anything as Collection is equal to Anything`: Unit = {
57-
def set = Set(1, 2, 3)
58-
def jset = new IterableWrapper(set)
59-
assertTrue(jset == jset)
60-
assertEquals(jset, jset)
61-
assertNotEquals(jset, set)
62-
assertEquals(jset.hashCode, jset.hashCode)
94+
@Test def `Iterable as JIterable does not compare equal`: Unit = {
95+
// scala iterable without element equality defined
96+
def iterable: Iterable[Int] = new AbstractIterable[Int] {
97+
override def iterator: Iterator[Int] = Iterator(1, 2, 3)
98+
}
99+
def jIterable = new IterableWrapper(iterable)
100+
assertNotEquals(jIterable, jIterable)
101+
assertNotEquals(jIterable.hashCode, jIterable.hashCode)
63102
}
64103

65-
@Test def `Iterator wrapper does not compare equal`: Unit = {
66-
def it = List(1, 2, 3).iterator
67-
def jit = new IteratorWrapper(it)
68-
assertNotEquals(jit, jit)
69-
assertNotEquals(jit.hashCode, jit.hashCode)
104+
@Test def `Iterator as JIterator does not compare equal`: Unit = {
105+
def iterator = Iterator(1, 2, 3)
106+
def jIterator = new IteratorWrapper(iterator)
107+
assertNotEquals(jIterator, jIterator)
108+
assertNotEquals(jIterator.hashCode, jIterator.hashCode)
70109
}
71110

72-
@Test def `Anything.asScala Iterable has case equals`: Unit = {
73-
def vs = jlstOf(42, 27, 37)
74-
def it = new JListWrapper(vs)
75-
assertEquals(it, it)
76-
assertEquals(it.hashCode, it.hashCode)
111+
@Test def `All wrapper compare equal if underlying is equal`(): Unit = {
112+
val jList = Collections.emptyList[String]()
113+
assertEquals(jList.asScala, jList.asScala)
114+
115+
val jIterator = jList.iterator()
116+
assertEquals(jIterator.asScala, jIterator.asScala)
117+
118+
val jEnumeration = Collections.emptyEnumeration[String]()
119+
assertEquals(jEnumeration.asScala, jEnumeration.asScala)
120+
121+
val jIterable = jList.asInstanceOf[JIterable[String]]
122+
assertEquals(jIterable.asScala, jIterable.asScala)
123+
124+
val jCollection = jList.asInstanceOf[JCollection[String]]
125+
assertEquals(jCollection.asScala, jCollection.asScala)
126+
127+
val jSet = Collections.emptySet[String]()
128+
assertEquals(jSet.asScala, jSet.asScala)
129+
130+
val jMap = Collections.emptyMap[String, String]()
131+
assertEquals(jMap.asScala, jMap.asScala)
132+
133+
val jCMap = new JCMap[String, String]()
134+
assertEquals(jCMap.asScala, jCMap.asScala)
135+
136+
val iterator = Iterator.empty[String]
137+
assertEquals(iterator.asJava, iterator.asJava)
138+
assertEquals(iterator.asJavaEnumeration, iterator.asJavaEnumeration)
139+
140+
val iterable = Iterable.empty[String]
141+
assertEquals(iterable.asJava, iterable.asJava)
142+
assertEquals(iterable.asJavaCollection, iterable.asJavaCollection)
143+
144+
val buffer = mutable.Buffer.empty[String]
145+
assertEquals(buffer.asJava, buffer.asJava)
146+
147+
val seq = mutable.Seq.empty[String]
148+
assertEquals(seq.asJava, seq.asJava)
149+
150+
val mutableSet = mutable.Set.empty[String]
151+
assertEquals(mutableSet.asJava, mutableSet.asJava)
152+
153+
val set = Set.empty[String]
154+
assertEquals(set.asJava, set.asJava)
155+
156+
val mutableMap = mutable.Map.empty[String, String]
157+
assertEquals(mutableMap.asJava, mutableMap.asJava)
158+
assertEquals(mutableMap.asJavaDictionary, mutableMap.asJavaDictionary)
159+
160+
val map = Map.empty[String, String]
161+
assertEquals(map.asJava, map.asJava)
162+
163+
val concurrentMap = concurrent.TrieMap.empty[String, String]
164+
assertEquals(concurrentMap.asJava, concurrentMap.asJava)
77165
}
78166
}

0 commit comments

Comments
 (0)