在Scala中,将类与常规类区分开来是否有任何时间或空间开销?



在Scala中使用case类与常规类相比是否有任何开销?它是否使用任何额外的内存,在构造中做更多的工作,或者在字段访问中做更多的工作?或者它实际上只是为类型层次结构底部的类提供免费的equals/hashcode/tostring/apply/unapply/等等?

我的用例是一个值得成为case类的类(如果所有字段都相等,则不可变且相等),但我在一个性能至关重要的领域。

(请不要回答"停止担心过早的优化")

Case类始终将其所有参数保持为字段。其他类只有在它们的参数被某个方法引用时才会这样做。这是我能想到的唯一性能差异,除了由于额外的方法而导致的更大的代码大小。

如果你编译一个最小的例子:

class Ordinary(val i: Int) { }
case class Case(i: Int) { }

你会发现普通类的字节码更小(~700 vs ~3500);你还会发现,在javap -c -private -v <Classname>中,构造函数有一个对Product trait初始化项的额外方法调用(它实际上没有做任何事情,因此应该由JIT编译器优化掉)。

所以对于重复使用一个类,它应该没有太大的区别。如果您有成千上万个这样的类,您可能会发现增加的字节码有问题。

第一个明显的原因是:由于创建了额外的方法,字节码变大了。

Case类保留第一个参数列表中的参数作为val成员。如果你不需要在对象的构造过程中访问参数,这是浪费的。

从Scala 2.8开始,case类还保留了后续参数节中的参数,以支持copy方法。这是一个实现细节,可能会发生变化。

scala> case class A(a: Int)(b: Int)
defined class A
scala> val a = A(0)(1)
a: A = A(0)
scala> a.copy()()
res9: A = A(0)
scala> :javap -private A
Compiled from "<console>"
public class A extends java.lang.Object implements scala.ScalaObject,scala.Product,scala.Serializable{
    private final int a;
    private final int b;
    public scala.collection.Iterator productIterator();
    public scala.collection.Iterator productElements();
    public int a();
    public A copy(int, int);
    public int copy$default$2(int);
    public int copy$default$1();
    public int hashCode();
    public java.lang.String toString();
    public boolean equals(java.lang.Object);
    public java.lang.String productPrefix();
    public int productArity();
    public java.lang.Object productElement(int);
    public boolean canEqual(java.lang.Object);
    private final boolean gd1$1(int);
    public A(int, int);
}

相关内容

最新更新