我正在开发一个应用程序,其中一个设计方法涉及到大量使用instanceof操作符。虽然我知道OO设计通常试图避免使用instanceof,但那是另一回事,这个问题纯粹与性能有关。我想知道是否对性能有影响?和==一样快吗?

例如,我有一个有10个子类的基类。在接受基类的单个函数中,我检查类是否是子类的实例并执行一些例程。

我想到的另一种解决方法是使用“type id”整数原语,并使用位掩码来表示子类的类别,然后对子类“type id”与表示类别的常量掩码进行位掩码比较。

instanceof是否被JVM优化得更快?我想坚持使用Java,但应用程序的性能至关重要。如果有人曾经在这条路上走过,可以提供一些建议,那就太棒了。我是不是太挑剔了,或者专注在错误的地方去优化?


当前回答

在大多数现实世界的实现中(也就是说,在真正需要instanceof的实现中,您不能像每个初学者教科书和上面的Demian所建议的那样,通过重写一个通用方法来解决它),instanceof可能会比简单的等号更昂贵。

为什么呢?因为可能会发生的情况是,你有几个接口,它们提供了一些功能(比如,接口x, y和z),以及一些要操作的对象,这些对象可能(或不)实现其中一个接口……但不是直接的。例如,我有:

W扩展x

A实现了w

B延伸A

C扩展B,实现y

D扩展C,实现z

假设我正在处理D的一个实例,对象D . Computing (D instanceof x)需要采用d.getClass(),循环通过它实现的接口来知道是否一个是==到x,如果不是这样做,再次递归为它们的所有祖先… 在我们的例子中,如果你对那棵树进行宽度优先的探索,假设y和z没有扩展任何东西,至少会产生8个比较……

现实世界的推导树的复杂性可能更高。在某些情况下,JIT可以优化其中的大部分,如果它能够在所有可能的情况下,将d解析为扩展x的某个实例。然而,实际上,您大部分时间都将遍历该树。

如果这成为一个问题,我建议使用处理程序映射,将对象的具体类链接到进行处理的闭包。它删除了树遍历阶段,以支持直接映射。但是,要注意,如果你为C.class设置了一个处理程序,上面的对象d将不会被识别。

这是我的2美分,我希望他们能帮助…

其他回答

如果它确实是项目中的性能问题,您应该进行测量/分析。如果是的话,我建议重新设计——如果可能的话。我很确定你不能打败平台的本机实现(用C编写)。在这种情况下,你还应该考虑多重继承。

你应该告诉更多关于这个问题,也许你可以使用一个关联存储,例如Map<Class,对象>如果你只对具体类型感兴趣。

Instanceof非常高效,因此您的性能不太可能受到影响。 然而,使用大量的instanceof暗示了一个设计问题。

如果您可以使用xClass == String.class,这将更快。注意:final类不需要instanceof。

我基于jmh-java-benchmark- prototype:2.21编写了一个性能测试。JDK为openjdk, version为1.8.0_212。测试机器是mac pro。 测试结果为:

Benchmark                Mode  Cnt    Score   Error   Units
MyBenchmark.getClasses  thrpt   30  510.818 ± 4.190  ops/us
MyBenchmark.instanceOf  thrpt   30  503.826 ± 5.546  ops/us

结果表明:getClass优于instanceOf,这与其他测试结果相反。然而,我不知道为什么。

测试代码如下:

public class MyBenchmark {

public static final Object a = new LinkedHashMap<String, String>();

@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean instanceOf() {
    return a instanceof Map;
}

@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean getClasses() {
    return a.getClass() == HashMap.class;
}

public static void main(String[] args) throws RunnerException {
    Options opt =
        new OptionsBuilder().include(MyBenchmark.class.getSimpleName()).warmupIterations(20).measurementIterations(30).forks(1).build();
    new Runner(opt).run();
}
}

Instanceof非常快。它可以归结为用于类引用比较的字节码。在一个循环中尝试几百万个实例,自己看看。

I'll get back to you on instanceof performance. But a way to avoid problem (or lack thereof) altogether would be to create a parent interface to all the subclasses on which you need to do instanceof. The interface will be a super set of all the methods in sub-classes for which you need to do instanceof check. Where a method does not apply to a specific sub-class, simply provide a dummy implementation of this method. If I didn't misunderstand the issue, this is how I've gotten around the problem in the past.