下面哪个比较好?

a instanceof B

or

B.class.isAssignableFrom(a.getClass())

我所知道的唯一区别是,当'a'为空时,第一个返回false,而第二个抛出异常。除此之外,它们总是给出相同的结果吗?


当前回答

谈论业绩:

博士TL;

请使用性能相似的isInstance或instanceof。isAssignableFrom稍微慢一点。

按性能排序:

isInstance Instanceof (+ 0.5%) isAssignableFrom (+ 2.7%)

基于JAVA 8 Windows x64上2000次迭代的基准测试,其中包括20次预热迭代。

在理论上

使用软的字节码查看器,我们可以将每个操作符转换为字节码。

在…的范围内:

package foo;

public class Benchmark
{
  public static final Object a = new A();
  public static final Object b = new B();

  ...

}

JAVA:

b instanceof A;

字节码:

getstatic foo/Benchmark.b:java.lang.Object
instanceof foo/A

JAVA:

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);

JAVA:

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

警告

the benchmark is JVM and platform dependent. Since there are no significant differences between each operation, it might be possible to get a different result (and maybe different order!) on a different JAVA version and/or platforms like Solaris, Mac or Linux. the benchmark compares the performance of "is B an instance of A" when "B extends A" directly. If the class hierarchy is deeper and more complex (like B extends X which extends Y which extends Z which extends A), results might be different. it is usually advised to write the code first picking one of the operators (the most convenient) and then profile your code to check if there are a performance bottleneck. Maybe this operator is negligible in the context of your code, or maybe... in relation to the previous point, instanceof in the context of your code might get optimized more easily than an isInstance for example...

给你一个例子,看下面的循环:

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,代码在某些时候被优化了,我们得到:

运算符:6女士 女士isInstance: 12 isAssignableFrom: 15毫秒

Note

最初这篇文章是在原始JAVA中使用for循环进行自己的基准测试,这给出了不可靠的结果,因为一些优化如Just in Time可以消除循环。因此主要是衡量JIT编译器优化循环所花费的时间:有关更多细节,请参阅与迭代次数无关的性能测试

相关问题

instanceof操作符是否产生大量开销?为什么? instanceof是如何在JAVA中实现的? 在Java中使用instanceof对性能的影响

其他回答

这完全取决于代码中此时可用的内容。我不建议使用isAssignableFrom如果你正在使用一个实际的对象-有一个更好的选择。下面是一份根据你所拥有的产品进行排名的推荐列表:

如果你有一个对象a,并且你在编译时知道类型B: a instanceof实例 如果你有一个对象a,你不知道类型B,但你有一个对象B: .isInstance b.getClass () () 如果你有一个对象a,在编译时没有类型或实例化对象,但你有一个类对象class <?> someBClass: someBClass.isInstance (a) 如果你没有实例化对象,但是你有两个Class<?>对象: someBClass.isAssignableFrom (someAClass)。

假设你从列表的顶部开始,然后往下做,每一种方法都是实现目标的最简单的方法,根据我自己的研究,我相信你也会得到最好的表现。

谈论业绩:

博士TL;

请使用性能相似的isInstance或instanceof。isAssignableFrom稍微慢一点。

按性能排序:

isInstance Instanceof (+ 0.5%) isAssignableFrom (+ 2.7%)

基于JAVA 8 Windows x64上2000次迭代的基准测试,其中包括20次预热迭代。

在理论上

使用软的字节码查看器,我们可以将每个操作符转换为字节码。

在…的范围内:

package foo;

public class Benchmark
{
  public static final Object a = new A();
  public static final Object b = new B();

  ...

}

JAVA:

b instanceof A;

字节码:

getstatic foo/Benchmark.b:java.lang.Object
instanceof foo/A

JAVA:

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);

JAVA:

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

警告

the benchmark is JVM and platform dependent. Since there are no significant differences between each operation, it might be possible to get a different result (and maybe different order!) on a different JAVA version and/or platforms like Solaris, Mac or Linux. the benchmark compares the performance of "is B an instance of A" when "B extends A" directly. If the class hierarchy is deeper and more complex (like B extends X which extends Y which extends Z which extends A), results might be different. it is usually advised to write the code first picking one of the operators (the most convenient) and then profile your code to check if there are a performance bottleneck. Maybe this operator is negligible in the context of your code, or maybe... in relation to the previous point, instanceof in the context of your code might get optimized more easily than an isInstance for example...

给你一个例子,看下面的循环:

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,代码在某些时候被优化了,我们得到:

运算符:6女士 女士isInstance: 12 isAssignableFrom: 15毫秒

Note

最初这篇文章是在原始JAVA中使用for循环进行自己的基准测试,这给出了不可靠的结果,因为一些优化如Just in Time可以消除循环。因此主要是衡量JIT编译器优化循环所花费的时间:有关更多细节,请参阅与迭代次数无关的性能测试

相关问题

instanceof操作符是否产生大量开销?为什么? instanceof是如何在JAVA中实现的? 在Java中使用instanceof对性能的影响

更直接的等价于B的实例是

B.class.isInstance(a)

当a为空时,这也可以工作(返回false)。

还有一个不同之处。如果要测试的类型(Class)是动态的,例如,作为方法参数传递,那么instanceof将不会为你切割它。

boolean test(Class clazz) {
   return (this instanceof clazz); // clazz cannot be resolved to a type.
}

但是你可以:

boolean test(Class clazz) {
   return (clazz.isAssignableFrom(this.getClass())); // okidoki
}

哎呀,这个答案已经讲过了。也许这个例子对某人有帮助。

还有另一个区别:

不管X是什么,null instanceof X都是假的

null.getClass().isAssignableFrom(X)将抛出NullPointerException