diff --git a/build.gradle.kts b/build.gradle.kts index 2ec2853..6073570 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { } group = "com.mapk" -version = "0.21" +version = "0.22" java { sourceCompatibility = JavaVersion.VERSION_1_8 diff --git a/src/main/kotlin/com/mapk/kmapper/KMapper.kt b/src/main/kotlin/com/mapk/kmapper/KMapper.kt index dc9461e..910cead 100644 --- a/src/main/kotlin/com/mapk/kmapper/KMapper.kt +++ b/src/main/kotlin/com/mapk/kmapper/KMapper.kt @@ -8,6 +8,8 @@ import com.mapk.core.getAliasOrName import com.mapk.core.isUseDefaultArgument import com.mapk.core.toKConstructor import java.lang.reflect.Method +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ConcurrentMap import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.KParameter @@ -31,7 +33,20 @@ class KMapper private constructor( .filter { it.kind != KParameter.Kind.INSTANCE && !it.isUseDefaultArgument() } .associate { (parameterNameConverter(it.getAliasOrName()!!)) to ParameterForMap.newInstance(it) } + private val getCache: ConcurrentMap, List<(Any, ArgumentBucket) -> Unit>> = ConcurrentHashMap() + private fun bindArguments(argumentBucket: ArgumentBucket, src: Any) { + val clazz = src::class + + // キャッシュヒットしたら登録した内容に沿って取得処理を行う + getCache[clazz]?.let { getters -> + // 取得対象フィールドは十分絞り込んでいると考えられるため、終了判定は行わない + getters.forEach { it(src, argumentBucket) } + return + } + + val tempCacheArrayList = ArrayList<(Any, ArgumentBucket) -> Unit>() + src::class.memberProperties.forEach outer@{ property -> // propertyが公開されていない場合は処理を行わない if (property.visibility != KVisibility.PUBLIC) return@outer @@ -46,14 +61,22 @@ class KMapper private constructor( if (it is KGetterAlias) alias = it.value } - parameterMap[alias ?: property.name]?.let { - // javaGetterを呼び出す方が高速 + parameterMap[alias ?: property.name]?.let { param -> javaGetter.isAccessible = true - argumentBucket.putIfAbsent(it.param, javaGetter.invoke(src)?.let { value -> it.mapObject(value) }) - // 終了判定 - if (argumentBucket.isInitialized) return + + val tempCache = { value: Any, bucket: ArgumentBucket -> + // 初期化済みであれば高コストな取得処理は行わない + if (!bucket.containsKey(param.param)) { + // javaGetterを呼び出す方が高速 + bucket.putIfAbsent(param.param, javaGetter.invoke(value)?.let { param.mapObject(it) }) + } + } + tempCache(src, argumentBucket) + tempCacheArrayList.add(tempCache) + // キャッシュの整合性を保つため、ここでは終了判定を行わない } } + getCache.putIfAbsent(clazz, tempCacheArrayList) } private fun bindArguments(argumentBucket: ArgumentBucket, src: Map<*, *>) { diff --git a/src/main/kotlin/com/mapk/kmapper/ParameterForMap.kt b/src/main/kotlin/com/mapk/kmapper/ParameterForMap.kt index 4dd300e..25d1875 100644 --- a/src/main/kotlin/com/mapk/kmapper/ParameterForMap.kt +++ b/src/main/kotlin/com/mapk/kmapper/ParameterForMap.kt @@ -1,6 +1,8 @@ package com.mapk.kmapper import com.mapk.core.EnumMapper +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ConcurrentMap import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.KParameter @@ -13,7 +15,7 @@ internal class ParameterForMap private constructor(val param: KParamete // リストの長さが小さいと期待されるためこの形で実装しているが、理想的にはmap的なものが使いたい private val converters: Set, KFunction>> = clazz.getConverters() - private val convertCache: MutableMap, (Any) -> Any?> = HashMap() + private val convertCache: ConcurrentMap, (Any) -> Any?> = ConcurrentHashMap() fun mapObject(value: U): Any? { val valueClazz: KClass<*> = value::class @@ -23,7 +25,7 @@ internal class ParameterForMap private constructor(val param: KParamete // パラメータに対してvalueが代入可能(同じもしくは親クラス)であればそのまま用いる if (clazz.isSuperclassOf(valueClazz)) { - convertCache[valueClazz] = { value } + convertCache.putIfAbsent(valueClazz) { it } return value } @@ -38,7 +40,7 @@ internal class ParameterForMap private constructor(val param: KParamete clazz == String::class -> { { it.toString() } } else -> throw IllegalArgumentException("Can not convert $valueClazz to $clazz") } - convertCache[valueClazz] = lambda + convertCache.putIfAbsent(valueClazz, lambda) return lambda(value) }