假设大型对象(在我的情况下是多兆字节的附件,每天数千个)通过REST端点接收并存储在这样的案例类中:
case class Box(largeBase64Object: String, results: List[String])
现在,此案例类的实例将在多个连续步骤(链)中处理。每个链步骤可以通过调用box.copy(results = "foo" :: box.results)
来改变实例(实际上,这个例子是简化的,它实际上是一个无形的HList,它存储每个步骤的结果)。
单步可能是,例如病毒扫描会将扫描程序的结果(感染/未感染Boolean
)添加到results
列表中。
但是,这种方法总是会创建潜在大型附件的新副本。是的,垃圾收集迟早会收集过时的副本,但我仍然害怕潜在的开销,因为我们谈论的是每天几千兆字节的附件数据。
另一个明显的方法是将附件存储在全局可变Map
中,并仅在Box
中存储引用。这样可以避免每步复制一次附件,但完全不可变的数据结构的漂亮属性将会消失。
处理这些情况的规范方法是什么?有没有人指出反映这种情况的基准(复制与全局存储)?
答案 0 :(得分:2)
案例类引用的对象不会被复制,只有案例类本身,所有引用都将与原始对象相同(除了那些你明确改变的对象)。
我们无法查看复制方法的源代码,因为它是由编译器生成的,但是我们可以使用-Xprint:typer
编译器标志来查看它生成的代码。
对于你的案例类
case class Box(largeBase64Object: String, results: List[String])
我们看到生成的方法(我正在使用scalac 2.12.3)
<synthetic> def copy(largeBase64Object: String = largeBase64Object, results: List[String] = results): Box = new Box(largeBase64Object, results);
<synthetic> def copy$default$1: String = Box.this.largeBase64Obj
<synthetic> def copy$default$2: List[String] = Box.this.results;
正如我们所看到的,copy方法只是使用它传递的对象创建case类的新实例,并且默认只是直接使用case类的字段而不进行任何类型的复制。