Reflection is suppose to be a bit time consuming on android. so i was wondering given a function reference like this:
fun isOdd(x: Int) = x % 2 != 0
val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd))
is ::isOdd call an unnecessary burden ?
would it be more efficient to not use them ?
UPDATE: doing some light metrics i did the following:
class MainActivity : Activity() {
val numbers = listOf(1, 2, 3)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
doRegular()
doReflection()
}
fun isOdd(x: Int) = x % 2 != 0
fun doRegular() {
val start = System.currentTimeMillis()
for (i in 0..999999) {
numbers.filter { it % 2 != 0 }
}
Log.v("myTag", "regular: +${System.currentTimeMillis() - start}")
}
fun doReflection() {
val start = System.currentTimeMillis()
for (i in 0..999999) {
numbers.filter(::isOdd)
}
Log.v("myTag", "reflection: ${System.currentTimeMillis() - start}")
}
}
and the print statement results are:
//*****this was the first attempt before compiler could optimise anything
regular: +577
reflection: 318
//*********
regular: +376
reflection: 327
regular: +593
reflection: 343
regular: +417
reflection: 251
regular: +243
reflection: 238
regular: +241
reflection: 238
regular: +238
reflection: 237
regular: +235
reflection: 247
regular: +237
reflection: 247
regular: +230
reflection: 268
What would you conclude given these results?
update: some are asking why i think its using reflection. its based on this:
This stackoverflow answer seems to state its reflection: and the title for the official doc has reflection as the main heading: hence my confusion.
Surely ::isOdd
is used for referencing the function, but unless it is really required to use the "reflection", it (reflection) is not used. Similar to how Int are changed into int
in the bytecode unless we need to store the reference, if we create collections then only it is represented as java.lang.Integer
.
The Kotlin compiler is smart and does the trick behind the hood.
/**
* Returns a list containing only elements matching the given [predicate].
*
* @sample samples.collections.Collections.Filtering.filter
*/
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
return filterTo(ArrayList<T>(), predicate)
}
The filter function is inline function that embeds the lambda/reference directly at the call-site without actually using the reflection.
If you see the JVM bytecode, you'll see the following decompiled Java code:
// of numbers.filter { it % 2 != 0 }
while(var6.hasNext()) {
Object element$iv$iv = var6.next();
int it = ((Number)element$iv$iv).intValue();
int var9 = false;
if (it % 2 != 0) { // <- check this out
destination$iv$iv.add(element$iv$iv);
}
}
// of numbers.filter(::isOdd)
while(var6.hasNext()) {
Object element$iv$iv = var6.next();
int p1 = ((Number)element$iv$iv).intValue();
int var9 = false;
if (isOdd(p1)) { // <- check this out
destination$iv$iv.add(element$iv$iv);
}
}
Realistically there is no reflection is involved here.
Sidenote: I've had a similar question over the official Kotlinlang slack at here, and I've got much attention but only 1 reply regarding it is only about metafacory as stated here in softwareengineering stackexchange subdomain.