Kotlin - 通过 id 或字符串获取 class 实例 属性 值的高性能和可扩展方式
Kotlin - Performant and scalable way to get class instance property value by id or by string
我想使 class 及其子 class 的属性在运行时可用于通过整数 ID 或 属性 的名称进行读写性能尽可能接近常规编译读取或写入的性能。 this class 和它的子 classes 可能有很多实例(比如最多 100 万),每个 class 可能有数百个属性,所以我想尽量减少使用的内存每个 class 实例中的每个 属性。
我看到的广泛的解决方案组正在使用反射,使每个 属性 成为可变 class 的实例,然后保留这些映射,或者编写巨大的 when 语句。
我测试了反射实现的性能(见下文)。这比在我的测试中直接访问 属性 花费的时间长 15 倍。
这可以改进吗,或者有更好的方法吗?
class ReflectionClass {
@FieldId(1)
var intField = 0
fun getPropById(id: Int): Any? {
val property = propertiesById[id]
return property?.get(this)
}
fun setIntPropById(id: Int, value: Int) {
val property = propertiesById[id]
if (property is KMutableProperty1) {
property?.setter?.call(this, value)
}
}
fun getPropByName(name: String): Any? {
val property = propertiesByName[name]
return property?.get(this)
}
fun setIntPropByName(name: String, value: Int) {
val property = propertiesByName[name]
if (property is KMutableProperty1) {
property as KMutableProperty1<ReflectionClass, Int>
property.set(this, value)
}
}
companion object {
//private val propertiesById = HashMap<Int, KProperty1<ReflectionClass,*>>()
private val propertiesById = HashMap<Int, KProperty1<ReflectionClass, *>?>()
private val propertiesByName = HashMap<String, KProperty1<ReflectionClass, *>>()
init {
val fields = ReflectionClass::class.memberProperties.forEach { property ->
val id = property.findAnnotation<FieldId>()
if (id != null) {
propertiesById.put(id.id, property)
propertiesByName.put(property.name, property)
}
}
}
}
}
我不认为你会从反思中获得你想要的表现。
(反射不是为高性能用途而设计的——根据我的经验,它很少用于生产代码。它非常适合测试、框架、构建工具等;但在大多数问题中我看看吧,真正的答案是使用不需要反射的更好的设计!)
当然,其他两种方法都不是完美的。这可能取决于确切的要求。这些是否需要是具有命名 Kotlin 属性的对象,或者它们可以一直是简单的映射吗?后者可能更易于编码和维护。否则硬编码测试可能会节省内存。
(如果你有很多时间,你可能会考虑编写某种构建工具,它可以自动为你生成带有那些硬编码测试的查找方法。That当然会使用反射,但仅在编译时使用。不过,这将是一项艰巨的工作,我不知道您将如何处理它。)
我想使 class 及其子 class 的属性在运行时可用于通过整数 ID 或 属性 的名称进行读写性能尽可能接近常规编译读取或写入的性能。 this class 和它的子 classes 可能有很多实例(比如最多 100 万),每个 class 可能有数百个属性,所以我想尽量减少使用的内存每个 class 实例中的每个 属性。
我看到的广泛的解决方案组正在使用反射,使每个 属性 成为可变 class 的实例,然后保留这些映射,或者编写巨大的 when 语句。
我测试了反射实现的性能(见下文)。这比在我的测试中直接访问 属性 花费的时间长 15 倍。
这可以改进吗,或者有更好的方法吗?
class ReflectionClass {
@FieldId(1)
var intField = 0
fun getPropById(id: Int): Any? {
val property = propertiesById[id]
return property?.get(this)
}
fun setIntPropById(id: Int, value: Int) {
val property = propertiesById[id]
if (property is KMutableProperty1) {
property?.setter?.call(this, value)
}
}
fun getPropByName(name: String): Any? {
val property = propertiesByName[name]
return property?.get(this)
}
fun setIntPropByName(name: String, value: Int) {
val property = propertiesByName[name]
if (property is KMutableProperty1) {
property as KMutableProperty1<ReflectionClass, Int>
property.set(this, value)
}
}
companion object {
//private val propertiesById = HashMap<Int, KProperty1<ReflectionClass,*>>()
private val propertiesById = HashMap<Int, KProperty1<ReflectionClass, *>?>()
private val propertiesByName = HashMap<String, KProperty1<ReflectionClass, *>>()
init {
val fields = ReflectionClass::class.memberProperties.forEach { property ->
val id = property.findAnnotation<FieldId>()
if (id != null) {
propertiesById.put(id.id, property)
propertiesByName.put(property.name, property)
}
}
}
}
}
我不认为你会从反思中获得你想要的表现。
(反射不是为高性能用途而设计的——根据我的经验,它很少用于生产代码。它非常适合测试、框架、构建工具等;但在大多数问题中我看看吧,真正的答案是使用不需要反射的更好的设计!)
当然,其他两种方法都不是完美的。这可能取决于确切的要求。这些是否需要是具有命名 Kotlin 属性的对象,或者它们可以一直是简单的映射吗?后者可能更易于编码和维护。否则硬编码测试可能会节省内存。
(如果你有很多时间,你可能会考虑编写某种构建工具,它可以自动为你生成带有那些硬编码测试的查找方法。That当然会使用反射,但仅在编译时使用。不过,这将是一项艰巨的工作,我不知道您将如何处理它。)