Java Scala枚举映射实现

Java Scala枚举映射实现,java,scala,Java,Scala,Java有一个特殊的Map实现,专为键属于enum类型的情况而设计(请参见类声明EnumMap)。例如,java.util.HashMap的主要优势在于内存和性能效率,因为它提前知道键的数量,因为它们在枚举中声明,所以内存紧凑且速度更快,因为不需要扩展内部表和解决哈希冲突 我想知道,除了Scala的枚举和immutable.Map接口之外,这个Java实现是否有现成的Scala实现。也许有任何外部lib实现,或者有任何方法可以创建自己的实现,并具有相近的内存和性能结果 非常感谢您提前提供的帮助

Java有一个特殊的
Map
实现,专为键属于
enum
类型的情况而设计(请参见类声明
EnumMap
)。例如,
java.util.HashMap
的主要优势在于内存和性能效率,因为它提前知道键的数量,因为它们在枚举中声明,所以内存紧凑且速度更快,因为不需要扩展内部表和解决哈希冲突

我想知道,除了Scala的
枚举
immutable.Map
接口之外,这个Java实现是否有现成的Scala实现。也许有任何外部lib实现,或者有任何方法可以创建自己的实现,并具有相近的内存和性能结果

非常感谢您提前提供的帮助

Enumeratum的公开发行版提供了一个信息丰富的基准

[info] MapComparisons.enumeratumScalaMapGet       avgt   30   9.830 ± 0.207  ns/op
[info] MapComparisons.jEnumEnumMapGet             avgt   30   4.745 ± 0.685  ns/op
[info] MapComparisons.jEnumScalaMapGet            avgt   30  12.204 ± 0.186  ns/op
劳埃德梅塔在哪里

所以目前的情况是Java EnumMap和EnumSet是 大约比普通的Scala集合和带有枚举数的映射快2倍,但是 由于我们仍在10纳秒以下的范围内,因此 说这里的性能不会成为瓶颈,除非是在super edge中 案例

我尝试复制lloydmeta的基准测试,并使用Scala 2.13、enumeratum 1.5.15获得以下结果

sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 -prof gc bench.So60374058"
平均时间,时间/op

enumeratumScalaMapGet   avgt   20     8.284 ±   0.071   ns/op
jEnumEnumMapGet         avgt   20     2.883 ±   0.023   ns/op
jEnumScalaMapGet        avgt   20     7.361 ±   0.273   ns/op
vanillaScalaMapGet      avgt   20     6.650 ±   0.323   ns/op
内存分配率

enumeratumScalaMapGet:·gc.alloc.rate   avgt   20  1753.262 ±  14.548  MB/sec
jEnumEnumMapGet:·gc.alloc.rate         avgt   20    ≈ 10⁻⁴            MB/sec
jEnumScalaMapGet:·gc.alloc.rate        avgt   20  1976.491 ±  70.259  MB/sec
vanillaScalaMapGet:·gc.alloc.rate      avgt   20  2190.644 ± 105.208  MB/sec
我们注意到,自2017年以来,
jEnumScalaMapGet
似乎已经赶上了
enumeratumScalaMapGet
以及
JenumumNummapGet
的琐碎内存分配

基准资料来源:

public enum JAgeGroup {
    Baby,
    Toddler,
    Teenager,
    Adult,
    Senior
}


@MarioGalic oh-wow上的信息基准,非常感谢您为我指出这一点。请用你提供的链接回答这个问题,这样我就可以投票并接受它了!谢谢。Scala还为小型不可变映射(最多包含4个元素)提供了特殊的实现,因此这些实现已经相当有效:@StephaneBersier谢谢您的评论。请原谅,也许我遗漏了什么,但正如名字所示,
EmptyMap
只是空映射的特殊实现。也许您想指向另一个实现,而不是那个实现?@IvanKurchenko如果您一直阅读
EmptyMap
的定义,您将看到
Map1
Map2
Map3
Map4
的定义。
import java.util
import java.util.concurrent.TimeUnit

import example.JAgeGroup
import org.openjdk.jmh.annotations._
import org.openjdk.jmh.infra.Blackhole
import enumeratum._

object AgeGroupVanilla extends Enumeration {
  type AgeGroupVanilla = Value
  val Baby, Toddler, Teenager, Adult, Senior = Value
}

sealed trait AgeGroup extends EnumEntry

case object AgeGroup extends Enum[AgeGroup] {

  val values = findValues

  case object Baby     extends AgeGroup
  case object Toddler  extends AgeGroup
  case object Teenager extends AgeGroup
  case object Adult    extends AgeGroup
  case object Senior   extends AgeGroup

}

@State(Scope.Thread)
@BenchmarkMode(Array(Mode.AverageTime))
@OutputTimeUnit(TimeUnit.NANOSECONDS)
class So60374058 {
  private val jEnumEnumMap = {
    val m: util.EnumMap[JAgeGroup, String] = new util.EnumMap(classOf[JAgeGroup])
    JAgeGroup.values().foreach(e => m.put(e, e.name()))
    m
  }

  private val jEnumScalaMap = Map(JAgeGroup.values().map(e => e -> e.name()): _*)

  private val ageGroupScalaMap = Map(AgeGroup.values.map(e => e -> e.entryName): _*)

  private val vanillaScalaMap = AgeGroupVanilla.values.map(e => e -> e.toString).toMap

  private def randomFrom[A](seq: Seq[A]): A = {
    seq(scala.util.Random.nextInt(seq.size))
  }

  private var jEnum: JAgeGroup       = _
  private var ageGroupEnum: AgeGroup = _
  private var vanillaEnum: AgeGroupVanilla.AgeGroupVanilla = _

  @Setup(Level.Trial)
  def setup(): Unit = {
    jEnum = randomFrom(JAgeGroup.values())
    ageGroupEnum = randomFrom(AgeGroup.values)
    vanillaEnum = randomFrom(AgeGroupVanilla.values.toSeq)
  }

  @Benchmark
  def jEnumEnumMapGet(bh: Blackhole): Unit = bh.consume {
    jEnumEnumMap.get(jEnum)
  }

  @Benchmark
  def jEnumScalaMapGet(bh: Blackhole): Unit = bh.consume {
    jEnumScalaMap.get(jEnum)
  }

  @Benchmark
  def enumeratumScalaMapGet(bh: Blackhole): Unit = bh.consume {
    ageGroupScalaMap.get(ageGroupEnum)
  }

  @Benchmark
  def vanillaScalaMapGet(bh: Blackhole): Unit = bh.consume {
    vanillaScalaMap.get(vanillaEnum)
  }
}