Говоря с точки зрения производительности:
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;
Bytecode:
getstatic foo/Benchmark.b:java.lang.Object
instanceof foo/A
ЯВА:
A.class.isInstance(b);
Bytecode:
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());
Bytecode:
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: 15ms
Запись
Первоначально эта статья делала свой собственный тест с использованием цикла for в сыром JAVA, который дал ненадежные результаты, так как некоторые оптимизации, такие как Just In Time, могут устранить цикл. Так что в основном измерения , сколько времени сделал взятие JIT компилятор для оптимизации цикла: см тест производительности зависит от числа итераций для получения более подробной информации
Смежные вопросы