From 26623f24310104fdd45bf52205a5df1815a7bd52 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Mon, 22 Jun 2020 14:27:21 +0200 Subject: [PATCH] Make isEmpty for IntMap / LongMap O(1) --- .../scala/collection/immutable/IntMap.scala | 2 +- .../scala/collection/immutable/LongMap.scala | 2 +- .../scala/tools/testkit/AssertUtil.scala | 12 +++++++ .../collection/immutable/IntMapTest.scala | 31 ++++++++++++++++++ .../collection/immutable/LongMapTest.scala | 32 +++++++++++++++++++ 5 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 test/junit/scala/collection/immutable/IntMapTest.scala create mode 100644 test/junit/scala/collection/immutable/LongMapTest.scala diff --git a/src/library/scala/collection/immutable/IntMap.scala b/src/library/scala/collection/immutable/IntMap.scala index f9a8e4233fc2..47116e0fcf4e 100644 --- a/src/library/scala/collection/immutable/IntMap.scala +++ b/src/library/scala/collection/immutable/IntMap.scala @@ -261,7 +261,7 @@ sealed abstract class IntMap[+T] extends AbstractMap[Int, T] override protected[this] def className = "IntMap" - override def isEmpty = this == IntMap.Nil + override def isEmpty = this eq IntMap.Nil override def knownSize: Int = if (isEmpty) 0 else super.knownSize override def filter(f: ((Int, T)) => Boolean): IntMap[T] = this match { case IntMap.Bin(prefix, mask, left, right) => { diff --git a/src/library/scala/collection/immutable/LongMap.scala b/src/library/scala/collection/immutable/LongMap.scala index 0d3524e6007b..d07b1277fe54 100644 --- a/src/library/scala/collection/immutable/LongMap.scala +++ b/src/library/scala/collection/immutable/LongMap.scala @@ -256,7 +256,7 @@ sealed abstract class LongMap[+T] extends AbstractMap[Long, T] override protected[this] def className = "LongMap" - override def isEmpty = this == LongMap.Nil + override def isEmpty = this eq LongMap.Nil override def knownSize: Int = if (isEmpty) 0 else super.knownSize override def filter(f: ((Long, T)) => Boolean): LongMap[T] = this match { case LongMap.Bin(prefix, mask, left, right) => { diff --git a/src/testkit/scala/tools/testkit/AssertUtil.scala b/src/testkit/scala/tools/testkit/AssertUtil.scala index 60e90bff975b..2efed2b4743c 100644 --- a/src/testkit/scala/tools/testkit/AssertUtil.scala +++ b/src/testkit/scala/tools/testkit/AssertUtil.scala @@ -253,6 +253,18 @@ object AssertUtil { def readyOrNot(awaitable: Awaitable[_]): Boolean = Try(Await.ready(awaitable, TestDuration.Standard)).isSuccess def withoutATrace[A](body: => A) = NoTrace(body) + + private lazy val modsField = classOf[Field].getDeclaredField("modifiers").tap(_.setAccessible(true)) + + def getFieldAccessible[T: ClassTag](n: String): Field = + implicitly[ClassTag[T]] + .runtimeClass.getDeclaredField(n) + .tap { f => + if ((f.getModifiers & Modifier.FINAL) != 0) + modsField.setInt(f, f.getModifiers() & ~Modifier.FINAL) + if ((f.getModifiers & Modifier.PUBLIC) == 0) + f.setAccessible(true) + } } object TestDuration { diff --git a/test/junit/scala/collection/immutable/IntMapTest.scala b/test/junit/scala/collection/immutable/IntMapTest.scala new file mode 100644 index 000000000000..b417fe75fabb --- /dev/null +++ b/test/junit/scala/collection/immutable/IntMapTest.scala @@ -0,0 +1,31 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.immutable + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.tools.testkit.AssertUtil.{getFieldAccessible => f} + +@RunWith(classOf[JUnit4]) +class IntMapTest { + @Test + def `isEmpty O(1)`(): Unit = { + val m = IntMap(1 -> (), 2 -> (), 3 -> ()) + f[IntMap.Bin[_]]("left").set(m, null) + f[IntMap.Bin[_]]("right").set(m, null) + assertFalse(m.isEmpty) // no NPE, does not access left or right + } +} diff --git a/test/junit/scala/collection/immutable/LongMapTest.scala b/test/junit/scala/collection/immutable/LongMapTest.scala new file mode 100644 index 000000000000..75bb1fdfdc9f --- /dev/null +++ b/test/junit/scala/collection/immutable/LongMapTest.scala @@ -0,0 +1,32 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.immutable + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.tools.testkit.AssertUtil.{getFieldAccessible => f} + +@RunWith(classOf[JUnit4]) +class LongMapTest { + + @Test + def `isEmpty O(1)`(): Unit = { + val m = LongMap(1l -> (), 2l -> (), 3l -> ()) + f[LongMap.Bin[_]]("left").set(m, null) + f[LongMap.Bin[_]]("right").set(m, null) + assertFalse(m.isEmpty) // no NPE, does not access left or right + } +}