Если говорить о производительности:
TL; DR
Используйте isInstance или instanceof с аналогичной производительностью. isAssignableFrom немного медленнее.
По производительности:
- isInstance
- instanceof (+ 0,5%)
- isAssignableFrom (+ 2,7%)
На основе теста производительности 2000 итераций на JAVA 8 Windows x64 с 20 итерациями разминки.
Теоретически
Используя такую программу, как просмотрщик байт-кода, мы можем преобразовать каждый оператор в байт-код.
В контексте:
package foo;
public class Benchmark
{
public static final Object a = new A();
public static final Object b = new B();
...
}
ЯВА:
b instanceof A;
Байт-код:
getstatic foo/Benchmark.b:java.lang.Object
instanceof foo/A
ЯВА:
A.class.isInstance(b);
Байт-код:
ldc Lfoo/A; (org.objectweb.asm.Type)
getstatic foo/Benchmark.b:java.lang.Object
invokevirtual java/lang/Class isInstance((Ljava/lang/Object;)Z);
ЯВА:
A.class.isAssignableFrom(b.getClass());
Байт-код:
ldc Lfoo/A; (org.objectweb.asm.Type)
getstatic foo/Benchmark.b:java.lang.Object
invokevirtual java/lang/Object getClass(()Ljava/lang/Class;);
invokevirtual java/lang/Class isAssignableFrom((Ljava/lang/Class;)Z);
Измеряя, сколько инструкций байт-кода используется каждым оператором, мы могли ожидать, что instanceof и isInstance будут быстрее, чем isAssignableFrom. Однако фактическая производительность определяется НЕ байт-кодом, а машинным кодом (который зависит от платформы). Сделаем микротест для каждого из операторов.
Контрольный показатель
Предоставлено: по совету @ aleksandr-dubinsky и благодаря @yura за предоставление базового кода, вот тест JMH (см. это руководство по настройке):
class A {}
class B extends A {}
public class Benchmark {
public static final Object a = new A();
public static final Object b = new B();
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean testInstanceOf()
{
return b instanceof A;
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean testIsInstance()
{
return A.class.isInstance(b);
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean testIsAssignableFrom()
{
return A.class.isAssignableFrom(b.getClass());
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(TestPerf2.class.getSimpleName())
.warmupIterations(20)
.measurementIterations(2000)
.forks(1)
.build();
new Runner(opt).run();
}
}
Дали следующие результаты (оценка - это количество операций в единицу времени, поэтому чем выше оценка, тем лучше):
Benchmark Mode Cnt Score Error Units
Benchmark.testIsInstance thrpt 2000 373,061 ± 0,115 ops/us
Benchmark.testInstanceOf thrpt 2000 371,047 ± 0,131 ops/us
Benchmark.testIsAssignableFrom thrpt 2000 363,648 ± 0,289 ops/us
Предупреждение
- эталонный тест зависит от JVM и платформы. Поскольку между каждой операцией нет значительных различий, возможно, можно будет получить другой результат (и, возможно, другой порядок!) На другой версии JAVA и / или платформах, таких как Solaris, Mac или Linux.
- тест сравнивает производительность «является ли B экземпляром A», когда «B расширяет A» напрямую. Если иерархия классов более глубокая и сложная (например, B расширяет X, который расширяет Y, который расширяет Z, который расширяет A), результаты могут быть другими.
- Обычно рекомендуется писать код, сначала выбирая один из операторов (наиболее удобный), а затем профилировать свой код, чтобы проверить, есть ли узкое место в производительности. Может быть, этот оператор незначителен в контексте вашего кода, а может ...
- по отношению к предыдущему пункту
instanceof в контексте вашего кода можно было бы оптимизировать легче, чем, например, isInstance ...
Чтобы дать вам пример, возьмите следующий цикл:
class A{}
class B extends A{}
A b = new B();
boolean execute(){
return A.class.isAssignableFrom(b.getClass());
// return A.class.isInstance(b);
// return b instanceof A;
}
// Warmup the code
for (int i = 0; i < 100; ++i)
execute();
// Time it
int count = 100000;
final long start = System.nanoTime();
for(int i=0; i<count; i++){
execute();
}
final long elapsed = System.nanoTime() - start;
Благодаря JIT код в какой-то момент оптимизируется и мы получаем:
- instanceof: 6 мсек
- isInstance: 12 мс
- isAssignableFrom: 15 мс
Примечание
Первоначально в этом посте проводился собственный тест с использованием цикла for в необработанном JAVA, что дало ненадежные результаты, поскольку некоторая оптимизация, такая как Just In Time, может устранить цикл. Таким образом, в основном это было измерение того, сколько времени потребовалось JIT-компилятору для оптимизации цикла: см. Тест производительности независимо от количества итераций, чтобы получить более подробную информацию.
Связанные вопросы
person
JBE
schedule
24.08.2012