Efficient map with case class as a key in Scala?

2019-06-24 20:01发布

问题:

A following C code uses enum and array as efficient "map" from enum to anything:

enum Color { ColorRed, ColorGreen, ColorBlue, ColorSize};


void f() {
  int x[ColorSize];
  x[ColorRed]   = 12;
  x[ColorGreen] = 33;
  x[ColorBlue]  = 4;
  return x[ColorGreen];
}

Is this possible with Scala?
I.e. to have a "map" from case class to something, implemented as efficient array and not as tree or as hashmap. Yet I would like to be able to index only with a paricular type not with Int.

Update: In short I would like to have Scala Array indexed by some kind of enum (case class or Enumeration).

回答1:

For small enumerations you can "simulate" the C behavior:

abstract sealed class Color(val index: Int)

object Color {
  implicit def col2int(color:Color) = color.index
}

case object ColorRed extends Color(0)
case object ColorGreen extends Color(1)
case object ColorBlue extends Color(2)

...

import Color._
val array = Array(1,2,3)
array(ColorRed) = 12

However, I doubt this would be considered good style, especially because it's unsafe. Using a map is a better approach, or you could wrap an array in a specialized data structure which deals with Color indizes:

class ColorArray[T:ClassManifest] {
  val array = new Array[T] (3)
  def apply(color: Color) = array(color.index)
  def update(color: Color, value: T) = array(color.index) = value
}

...

val cArray = new ColorArray[Int]()
cArray(ColorRed) = 12
println(cArray(ColorRed))


回答2:

object Color extends Enumeration{
  val ColorRed, ColorGreen, ColorBlue = Value
}

import Color._
def f:Map[Color.Value,Int] = 
  Map(ColorRed -> 12 , ColorGreen -> 33, ColorBlue -> 4)



回答3:

If you want the full C performance you could do this:

trait CEnum {
private var size = 0;
def value = { size += 1; size-1 }
}

object Color extends CEnum {
  val colorRed = value 
  val colorGreen = value 
  val colorBlue = value 
  val colorSize = 3
}

import Color._

def f() = {
  val x = Array[Int](colorSize)
  x(colorRed) = 12
  x(colorGreen) = 33
  x(colorBlue) = 4
  x(colorGreen)
}

It's equally unsafe as the method in C & just as performant. It is however very unsafe.