0

I want to check if a specify id that contained in an Enumeration.
So I write down the contains function

object Enum extends Enumeration {
  type Enum = Value
  val A = Value(2, "A")

  def contains(value: Int): Boolean = {
    Enum.values.map(_.id).contains(value)
  }
}

But the time cost is unexpected while id is a big number, such as over eight-digit

val A = Value(222222222, "A")

Then the contains function cost over 1000ms per calling.

And I also noticed the first time calling always cost hundreds millisecond whether the id is big or small.

I can't figure out why.

Joe
  • 3,581
  • 4
  • 23
  • 28

2 Answers2

2

First, lets talk about the cost of Enum.values. This is implemented here:

See here: https://github.com/scala/scala/blob/0b47dc2f28c997aed86d6f615da00f48913dd46c/src/library/scala/Enumeration.scala#L83

The implementation is essentially setting up a mutable map. Once it is set up, it is re-used.

The cost for big numbers in your Value is because, internally Scala library uses a BitSet.

See here: https://github.com/scala/scala/blob/0b47dc2f28c997aed86d6f615da00f48913dd46c/src/library/scala/Enumeration.scala#L245

So, for larger numbers, BitSet will be bigger. That only happens when you call Enum.values.

Depending on your specific uses case you can choose between using Enumeration or Case Object:

Community
  • 1
  • 1
tuxdna
  • 8,257
  • 4
  • 43
  • 61
1

It sure looks like the mechanics of Enumeration don't handle large ints well in that position. The Scaladocs for the class don't say anything about this, but they don't advertise using Enumeration.Value the way you do either. They say, e.g., val A = Value, where you say val A = Value(2000, "A").

If you want to keep your contains method as you have it, why don't you cache the Enum.values.map(_.id)? Much faster.

object mult extends App {

  object Enum extends Enumeration {
    type Enum = Value
    val A1 = Value(1, "A")
    val A2 = Value(2, "A")
    val A222 = Enum.Value(222222222, "A")
    def contains(value: Int): Boolean = {
      Enum.values.map(_.id).contains(value)
    }
    val cache = Enum.values.map(_.id)
    def contains2(value: Int): Boolean = {
      cache.contains(value)
    }
  }

  def clockit(desc: String, f: => Unit) = {
    val start = System.currentTimeMillis
    f
    val end = System.currentTimeMillis
    println(s"$desc ${end - start}")
  }
  clockit("initialize Enum    ", Enum.A1)
  clockit("contains 2         ", Enum.contains(2))
  clockit("contains 222222222 ", Enum.contains(222222222))
  clockit("contains 222222222 ", Enum.contains(222222222))
  clockit("contains2 2        ", Enum.contains2(2))
  clockit("contains2 222222222", Enum.contains2(222222222))
}
john sullivan
  • 1,898
  • 2
  • 13
  • 21