在Java中使用instanceof的性能影响


314

我正在开发一个应用程序,一种设计方法涉及大量使用instanceof操作员。虽然我知道OO设计通常会尝试避免使用instanceof,但这是另一回事了,这个问题与性能完全相关。我想知道是否会对性能产生影响?是一样快==吗?

例如,我有一个包含10个子类的基类。在采用基类的单个函数中,我检查该类是否为子类的实例并执行一些例程。

我想解决的另一种方法是改用“类型id”整数基元,并使用位掩码表示子类的类别,然后将子类“类型id”与位掩码进行位掩码比较。代表类别的常量掩码。

instanceofJVM 是否以某种方式对其进行了优化,使其速度更快?我想坚持使用Java,但应用程序的性能至关重要。如果以前曾走过这条路的人可以提供一些建议,那将很酷。我是否挑剔或专注于错误的东西进行优化?


81
但是,我认为问题的重点是搁置最佳OO实践问题,并检查性能。
戴夫

3
@DaveL。通常我会同意,但是OP确实提到他正在寻找一些通用的优化技术,但他不确定他的问题是否与'instanceof'有关。我认为至少值得一提“正确”的设计,这样他就可以同时描述这两种选择。
Outlaw程序员

51
gh ...为什么所有答案都遗漏了问题的要点,却提供了同样的关于优化的老Knuth言论?您的问题是关于instanceof是否比使用==检查类对象显着/令人惊讶地慢,我发现事实并非如此。
gubby 2010年

3
instanceof和cast的性能相当不错。我张贴在Java7各地不同的做法问题的一些时间在这里:stackoverflow.com/questions/16320014/...
Wheezil

Answers:


268

现代的JVM / JIC编译器已消除了大多数传统的“慢速”操作的性能影响,其中包括instanceof,异常处理,反射等。

正如Donald Knuth所说:“我们应该忘记效率低下的问题,大约有97%的时间是这样:过早的优化是万恶之源。” instanceof的性能可能不会成为问题,因此在确定这是问题之前,请不要浪费时间来寻求新的解决方法。


13
现代的JVM / JIC ..请您指出这些优化是从哪个Java版本开始的?
拉维沙

138
总是会有人在性能是主题时引用Knuth ...忘记了,Knuth还说过(在同一篇文章中)“在既定的工程学科中,容易实现的12%的改进从未被认为是微不足道的,我相信相同的观点应该在软件工程中占上风”,他几乎所有的工作都是关于算法效率的,并且他编写了汇编算法以(尤其)获得更好的性能。嗯...
kgadek'9

4
放在这里,但会try { ObjT o = (ObjT)object } catch (e) { no not one of these }更快一些吗?
彼得

35
如果“对象”是ObjT的实例,则强制转换比执行instanceof快一点,但是我的快速测试发现,在10,000,000次迭代中,差异为10-20ms。但是,如果“对象”不是ObjT,则捕获异常的速度要慢3000倍-超过31,000毫秒,而instanceof则约为10毫秒。
史蒂夫

19
如此强硬的论点,没有任何“参考”,完全是无用的,因为只是自以为是。
marcorossi 2013年

278

方法

我编写了一个基准程序来评估不同的实现:

  1. instanceof 实施(作为参考)
  2. 通过抽象类和@Override测试方法定位的对象
  3. 使用自己的类型实现
  4. getClass() == _.class 实作

我使用jmh进行了100次预热调用,正在测量的1000次迭代以及10次fork来运行基准测试。因此,每个选项的测量结果为1万次,这需要12:18:57才能在具有macOS 10.12.4和Java 1.8的MacBook Pro上运行整个基准测试。基准衡量每个选项的平均时间。有关更多详细信息,请参见我在GitHub上的实现

为了完整起见:该答案有一个较早的版本和我的基准

结果

| 操作| 每个操作的运行时间(以纳秒为单位)相对于instanceof |
| ------------ | ---------------------------------------- -| ------------------------ |
| 实例 39,598±0,022 ns / op | 100,00%|
| GETCLASS | 39,687±0,021 ns / op | 100,22%|
| TYPE | 46,295±0,026 ns / op | 116,91%|
| OO | 48,078±0,026 ns / op | 121,42%|

tl; dr

instanceof尽管getClass()非常接近,但在Java 1.8中是最快的方法。


58
+0.(9)为了科学!

16
+我的其他0.1:D
Tobias Reich

14
@TobiasReich这样我们就知道了+1.0(9)。:)
Pavel

9
我认为这根本没有任何有意义的措施。该代码System.currentTimeMillis()仅在单个方法调用上测量一个操作上的使用情况,这将大大降低精度。请改用诸如JMH之类的基准框架!
Lii 2015年

6
或者只执行整个十亿次呼叫的时间,而不是每次呼叫的时间。
LegendLength

74

我只是做了一个简单的测试,以查看instanceOf性能与仅对一个字母的字符串对象进行简单的s.equals()调用相比。

在10.000.000循环中,instanceOf给了我63-96ms,而字符串equals给了我106-230ms

我使用了Java jvm 6。

因此,在我的简单测试中,执行instanceOf而不是比较一个字符串会更快。

仅当我使用== i比instanceOf快20ms(在10.000.000循环中)时,使用Integer的.equals()而不是string的结果相同。


4
您可以在此处发布代码吗?那将是真棒!
炼金术士2010年

7
instanceOf与多态函数调度相比如何?
克里斯(Chris

21
为什么将instanceof与String.equals()比较?如果要检查类型,则必须使用object.getClass()。equals(SomeType.class)
marsbear 2011年

4
@marsbear equals()不会削减它,因为子类化;你需要isAssignableFrom()
大卫·摩尔

1
@marsbear对,但这对OP的要求没有更好的检验。
大卫·摩尔

20

决定性能影响的项目有:

  1. instanceof运算符可以返回true的可能类的数量
  2. 您的数据分布-在第一次或第二次尝试中是否解决了大多数instanceof操作?您将想把最有可能返回真实操作的位置放在首位。
  3. 部署环境。在Sun Solaris VM上运行与Windows Windows JVM明显不同。默认情况下,Solaris将以“服务器”模式运行,而Windows将以客户端模式运行。Solaris上的JIT优化将使所有方法访问均相同。

为四种不同的调度方法创建了一个微基准。Solaris的结果如下,数值越小速度越快:

InstanceOf 3156
class== 2925 
OO 3083 
Id 3067 

18

回答最后一个问题:除非探查员告诉您,否则您在以下情况下花费了荒谬的时间:是的,您在挑剔。

在想知道不需要优化的东西之前:以最易读的方式编写算法并运行它。运行它,直到jit编译器有机会对其自身进行优化。如果您随后对这段代码有疑问,请使用探查器来告诉您在哪里获取最大收益并对其进行优化。

在高度优化的编译器时代,您对瓶颈的猜测可能完全是错误的。

按照这个答案的真实精神(我完全相信):一旦jit编译器有机会优化它,我绝对不知道instanceof和==的关系。

我忘记了:永远不要衡量第一轮。


1
但是最初的发布者提到性能对于此应用程序至关重要,因此在这种情况下尽早进行优化并非没有道理。换句话说,您不会用GWBasic编写3D游戏,然后说,好,让我们开始优化它,第一步是将其移植到c ++。
LegendLength

如果有合适的库,GWBasic可能是3d游戏的一个不错的开始。但是,除此之外(因为这是一个人为的论点):OP并没有要求将完整的重写作为优化。它是关于单个构造的,我们甚至都不知道影响是否重大(即使在当前版本的编译器中,有更好的执行方法可以执行相同的操作)。我坚定地站在c2.com/cgi/wiki?ProfileBeforeOptimizing和我的答案后面。初步优化是万恶之源!这使得维护更难-和维护是一个值得优化的方面
奥拉夫·科克

15

我有相同的问题,但是因为我没有找到与我的用例类似的“性能指标”,所以我做了一些示例代码。在我的硬件和Java 6&7上,instanceof和10mmn迭代之间的区别是

for 10 child classes - instanceof: 1200ms vs switch: 470ms
for 5 child classes  - instanceof:  375ms vs switch: 204ms

因此,instanceof确实很慢,尤其是在大量的if-else-if语句上,但是在实际应用程序中的差异可以忽略不计。

import java.util.Date;

public class InstanceOfVsEnum {

    public static int c1, c2, c3, c4, c5, c6, c7, c8, c9, cA;

    public static class Handler {
        public enum Type { Type1, Type2, Type3, Type4, Type5, Type6, Type7, Type8, Type9, TypeA }
        protected Handler(Type type) { this.type = type; }
        public final Type type;

        public static void addHandlerInstanceOf(Handler h) {
            if( h instanceof H1) { c1++; }
            else if( h instanceof H2) { c2++; }
            else if( h instanceof H3) { c3++; }
            else if( h instanceof H4) { c4++; }
            else if( h instanceof H5) { c5++; }
            else if( h instanceof H6) { c6++; }
            else if( h instanceof H7) { c7++; }
            else if( h instanceof H8) { c8++; }
            else if( h instanceof H9) { c9++; }
            else if( h instanceof HA) { cA++; }
        }

        public static void addHandlerSwitch(Handler h) {
            switch( h.type ) {
                case Type1: c1++; break;
                case Type2: c2++; break;
                case Type3: c3++; break;
                case Type4: c4++; break;
                case Type5: c5++; break;
                case Type6: c6++; break;
                case Type7: c7++; break;
                case Type8: c8++; break;
                case Type9: c9++; break;
                case TypeA: cA++; break;
            }
        }
    }

    public static class H1 extends Handler { public H1() { super(Type.Type1); } }
    public static class H2 extends Handler { public H2() { super(Type.Type2); } }
    public static class H3 extends Handler { public H3() { super(Type.Type3); } }
    public static class H4 extends Handler { public H4() { super(Type.Type4); } }
    public static class H5 extends Handler { public H5() { super(Type.Type5); } }
    public static class H6 extends Handler { public H6() { super(Type.Type6); } }
    public static class H7 extends Handler { public H7() { super(Type.Type7); } }
    public static class H8 extends Handler { public H8() { super(Type.Type8); } }
    public static class H9 extends Handler { public H9() { super(Type.Type9); } }
    public static class HA extends Handler { public HA() { super(Type.TypeA); } }

    final static int cCycles = 10000000;

    public static void main(String[] args) {
        H1 h1 = new H1();
        H2 h2 = new H2();
        H3 h3 = new H3();
        H4 h4 = new H4();
        H5 h5 = new H5();
        H6 h6 = new H6();
        H7 h7 = new H7();
        H8 h8 = new H8();
        H9 h9 = new H9();
        HA hA = new HA();

        Date dtStart = new Date();
        for( int i = 0; i < cCycles; i++ ) {
            Handler.addHandlerInstanceOf(h1);
            Handler.addHandlerInstanceOf(h2);
            Handler.addHandlerInstanceOf(h3);
            Handler.addHandlerInstanceOf(h4);
            Handler.addHandlerInstanceOf(h5);
            Handler.addHandlerInstanceOf(h6);
            Handler.addHandlerInstanceOf(h7);
            Handler.addHandlerInstanceOf(h8);
            Handler.addHandlerInstanceOf(h9);
            Handler.addHandlerInstanceOf(hA);
        }
        System.out.println("Instance of - " + (new Date().getTime() - dtStart.getTime()));

        dtStart = new Date();
        for( int i = 0; i < cCycles; i++ ) {
            Handler.addHandlerSwitch(h1);
            Handler.addHandlerSwitch(h2);
            Handler.addHandlerSwitch(h3);
            Handler.addHandlerSwitch(h4);
            Handler.addHandlerSwitch(h5);
            Handler.addHandlerSwitch(h6);
            Handler.addHandlerSwitch(h7);
            Handler.addHandlerSwitch(h8);
            Handler.addHandlerSwitch(h9);
            Handler.addHandlerSwitch(hA);
        }
        System.out.println("Switch of - " + (new Date().getTime() - dtStart.getTime()));
    }
}

哪个结果是Java 6,哪个是Java 7?您是否在Java 8下重访了此内容?更重要的是,您正在将instanceofs的长度与int的case语句的基本内容进行比较。我认为我们期望int开关会很快变得轻快。
Azeroth2b

1
我不记得5年前到底发生了什么-我认为Java 6和Java 7都有类似的结果,这就是为什么只提供一个结果的原因(假设两行是针对不同层次的类层次结构的)...而且没有,我没有尝试与Java 8进行比较。提供了完整的测试代码-您可以复制/粘贴它,并在所需的环境中进行检查(请注意-如今,我将为此使用JMH测试)。
Xtra Coder

9

instanceof 速度非常快,只需要几个CPU指令。

显然,如果一个类X没有加载子类(JVM知道),则instanceof可以将其优化为:

     x instanceof X    
==>  x.getClass()==X.class  
==>  x.classID == constant_X_ID

主要成本只是阅读!

如果X确实加载了子类,则还需要读取一些内容。它们很可能位于同一地点,因此额外成本也很低。

大家好消息!


2
可以优化还是优化?资源?

@vaxquis 可以将其特定于jvm的方式实现
RecursiveExceptionException

@itzJanuary 感叹你错过了这里我的问题点:大家都知道,编译器可以优化foo-但foo实际上目前甲骨文的javac / VM优化-或者只是可能的,它会做,在未来?另外,我问回答者他是否有任何后备源(文档,源代码,开发博客)证明确实可以优化或优化?没有它,这个答案就是什么编译器的一些随机的沉思可以可能做到。

@vaxquis您从未提到过Hotspot VM,但是在那种情况下,我不知道它是否“经过优化”。
RecursiveExceptionException

1
最近读到,JIT(JVM 8)将通过直接调用针对1或2种类型优化调用站点,但是如果遇到两种以上实际类型,则会恢复为vtable。因此,只有两种具体类型在运行时通过呼叫站点才能代表性能优势。
simon.watts

5

Instanceof非常快。它归结为用于类引用比较的字节码。循环尝试几百万个instanceofs,自己看看。


5

在大多数现实世界的实现中,instanceof可能会比简单的等价物更为昂贵(也就是说,确实需要instanceof的实现,而您不能仅仅通过覆盖一个通用方法来解决它,就像每本初学者教科书以及上面的Demian建议)。

这是为什么?因为可能要发生的事情是您有几个接口,它们提供了一些功能(例如,接口x,y和z),以及一些要操作的对象,这些对象可能(也可能不)实现这些接口之一...但是不直接。举例来说,我有:

w延伸x

一个工具w

B延伸A

C扩展B,实现y

D扩展C,实现z

假设我正在处理D的一个实例,即对象d。计算(x instanceof x)需要采用d.getClass(),在其实现的接口中循环以了解x是否等于==,如果不是,则对所有祖先再次递归执行……在我们的情况下,如果您对该树进行广度优先探索,则至少进行8次比较,假设y和z不扩展任何内容...

实际派生树的复杂性可能会更高。在某些情况下,如果JIT能够提前将d解析为在所有可能的情况下都是扩展x的实例,则JIT可以对其进行优化。实际上,大多数情况下,您将经历该树遍历。

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

这是我的2美分,希望他们能帮忙...


5

instanceof非常有效,因此您的性能不太可能受到影响。但是,使用大量instanceof会提出一个设计问题。

如果可以使用xClass == String.class,则速度更快。注意:最终课程不需要instanceof。


1
顺便说一句,“不需要为最终课程使用instanceof”是什么意思?
Pacerier 2012年

最终班不能有子班。在这种情况下x.getClass() == Class.classx instanceof Class
Peter Lawrey

太酷了,假设x不为null,您想要哪个?
Pacerier 2012年

好点子。这取决于我是否想x成为null我的假设。(或者更清晰的地方)
彼得·劳里

嗯,我刚刚意识到我们也可以使用java.lang.class.isAssignableFrom,您是否知道instanceof关键字在内部使用此类函数?
Pacerier,2012年

4

通常,在这种情况下(在instanceof正在检查此基类的子类的情况下)不赞成使用“ instanceof”运算符的原因是,因为您应该执行的操作是将操作移至方法中并覆盖相应的方法子类。例如,如果您有:

if (o instanceof Class1)
   doThis();
else if (o instanceof Class2)
   doThat();
//...

您可以将其替换为

o.doEverything();

然后在Class1中调用“ doThis()”,并在Class2中调用“ doThat()”,以实现“ doEverything()”,依此类推。


11
但是有时候你做不到。如果要实现一个要带入一个Object的接口,并且需要告诉它是哪种类型,那么instanceof实际上是唯一的选择。您可以尝试强制转换,但是instanceof通常更干净。
爱马仕

4

“ instanceof”实际上是一个运算符,例如+或-,我相信它具有自己的JVM字节码指令。它应该足够快。

我不应该说,如果有一个开关在测试对象是否是某个子类的实例,那么可能需要重新设计。考虑将特定于子类的行为推入子类本身。


4

德米安和保罗提到了一个要点。但是,执行代码的位置实际上取决于您要如何使用数据...

我非常喜欢可以以多种方式使用的小数据对象。如果遵循覆盖(多态)方法,则只能“单向”使用对象。

这是模式出现的地方...

您可以使用两次发送(如访问者模式)来要求每个对象通过自身传递给您“呼叫”,这将解决对象的类型。但是(再次),您将需要一个可以对所有可能的子类型“做事”的类。

我更喜欢使用策略模式,您可以在其中为要处理的每个子类型注册策略。类似于以下内容。请注意,这仅有助于精确的类型匹配,但具有可扩展的优点-第三方贡献者可以添加自己的类型和处理程序。(这对于像OSGi这样的动态框架很有用,可以在其中添加新的捆绑软件)

希望这会激发其他一些想法...

package com.javadude.sample;

import java.util.HashMap;
import java.util.Map;

public class StrategyExample {
    static class SomeCommonSuperType {}
    static class SubType1 extends SomeCommonSuperType {}
    static class SubType2 extends SomeCommonSuperType {}
    static class SubType3 extends SomeCommonSuperType {}

    static interface Handler<T extends SomeCommonSuperType> {
        Object handle(T object);
    }

    static class HandlerMap {
        private Map<Class<? extends SomeCommonSuperType>, Handler<? extends SomeCommonSuperType>> handlers_ =
            new HashMap<Class<? extends SomeCommonSuperType>, Handler<? extends SomeCommonSuperType>>();
        public <T extends SomeCommonSuperType> void add(Class<T> c, Handler<T> handler) {
            handlers_.put(c, handler);
        }
        @SuppressWarnings("unchecked")
        public <T extends SomeCommonSuperType> Object handle(T o) {
            return ((Handler<T>) handlers_.get(o.getClass())).handle(o);
        }
    }

    public static void main(String[] args) {
        HandlerMap handlerMap = new HandlerMap();

        handlerMap.add(SubType1.class, new Handler<SubType1>() {
            @Override public Object handle(SubType1 object) {
                System.out.println("Handling SubType1");
                return null;
            } });
        handlerMap.add(SubType2.class, new Handler<SubType2>() {
            @Override public Object handle(SubType2 object) {
                System.out.println("Handling SubType2");
                return null;
            } });
        handlerMap.add(SubType3.class, new Handler<SubType3>() {
            @Override public Object handle(SubType3 object) {
                System.out.println("Handling SubType3");
                return null;
            } });

        SubType1 subType1 = new SubType1();
        handlerMap.handle(subType1);
        SubType2 subType2 = new SubType2();
        handlerMap.handle(subType2);
        SubType3 subType3 = new SubType3();
        handlerMap.handle(subType3);
    }
}

4

我写了一个基于jmh-java-benchmark-archetype:2.21的性能测试。JDK是openjdk,版本是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的作用可以说是更为复杂。getClass()==检查将执行精确的1:1检查,其中instanceof检查层次结构,即myHashSet instanceof Collection将通过,而myHashSet.getClass()== Collection.class将不通过。从本质上讲,它们不是等效的操作,因此对于性能也有所不同,我并不感到惊讶。
6

3

很难说某个JVM是如何实现其实例的,但是在大多数情况下,对象可与结构媲美,而类也可与之媲美,并且每个对象结构都有一个指向其实例的类结构的指针。所以实际上是instanceof

if (o instanceof java.lang.String)

可能与以下C代码一样快

if (objectStruct->iAmInstanceOf == &java_lang_String_class)

假设有一个JIT编译器并且做得不错。

考虑到这只是访问一个指针,将指针指向该指针指向的特定偏移量,然后将其与另一个指针进行比较(这与测试32位数字是否相等基本相同),我想说该操作实际上可以很快

但是,它不必一定取决于JVM。但是,如果这将成为代码中的瓶颈操作,那么我认为JVM的实现相当糟糕。即使是没有JIT编译器且仅解释代码的程序,也几乎可以在短时间内进行测试。


1
不必确定o是否从java.lang.String继承吗?
WW。

1
这就是为什么我说它“可能”那么快。实际上,它执行一个循环,首先针对相关类检查iAmInstanceOf,然后向上执行o的继承树,并对o的每个超类重复此检查(因此它可能必须运行此循环几次)比赛)
Mecki,2009年

3

我将在性能方面与您联系。但是,一种完全避免问题(或没有问题)的方法是为需要执行instanceof的所有子类创建一个父接口。该接口将是您需要对其进行instanceof检查的子类中所有方法的超集。如果某个方法不适用于特定的子类,则只需提供此方法的虚拟实现即可。如果我没有误解这个问题,那么这就是我过去解决这个问题的方式。


2

实例是对不良的面向对象设计的警告。

当前的JVM确实意味着 instanceOf本身并没有太多的性能问题。如果您发现自己经常使用它,尤其是对于核心功能,则可能是时候看一下设计了。重构为更好的设计所获得的性能(以及简单性/可维护性)收益将大大超过花费在实际instanceOf上的任何实际处理器周期。调用。

给出一个非常简单的编程示例。

if (SomeObject instanceOf Integer) {
  [do something]
}
if (SomeObject instanceOf Double) {
  [do something different]
}

如果架构较差,那么更好的选择是让SomeObject作为两个子类的父类,其中每个子类都覆盖一个方法(doSomething),因此代码如下所示:

Someobject.doSomething();

61
我对此很清楚。那不是我问的。
乔什(Josh)

不确定是否赞成这一点,因为这是一个好点,但没有回答所提出的问题...
jklp

7
我认为代码示例实际上是一个非常糟糕的示例:您不能扩展Double类,也不能从其他类派生Double。如果您使用其他类作为示例,那就没问题了。
Lena Schimmel 2009年

6
同样,如果SomeObject的子类是值对象,那么您也不想在其中放入逻辑。例如,Pie和Roast可能不是putInOven()和putInMouth()逻辑的正确位置。
sk。

自炊派和烤会真棒
binboavetonik

2

在现代Java版本中,instanceof运算符作为一个简单的方法调用速度更快。这表示:

if(a instanceof AnyObject){
}

更快,因为:

if(a.getType() == XYZ){
}

另一件事是,如果您需要级联许多instanceof。然后,仅调用一次getType()的开关会更快。


1

如果速度是您的唯一目标,则使用int常量标识子类似乎节省了毫秒的时间

static final int ID_A = 0;
static final int ID_B = 1;
abstract class Base {
  final int id;
  Base(int i) { id = i; }
}
class A extends Base {
 A() { super(ID_A); }
}
class B extends Base {
 B() { super(ID_B); }
}
...
Base obj = ...
switch(obj.id) {
case  ID_A: .... break;
case  ID_B: .... break;
}

糟糕的OO设计,但是如果您的性能分析表明这是瓶颈,那么也许是。在我的代码中,调度代码占用了总执行时间的10%,这可能有助于总速度提高1%。


0

如果这确实是项目中的性能问题,则应进行度量/配置。如果可以的话,我建议您重新设计-如果可能的话。我很确定您不会击败平台的本机实现(用C编写)。在这种情况下,您还应该考虑多重继承。

您应该更多地介绍该问题,如果您仅对具体类型感兴趣,也许可以使用关联存储,例如Map <Class,Object>。


0

关于Peter Lawrey的注释,您不需要为最终类使用instanceof,而只需使用引用相等,请当心!即使不能扩展最终的类,也不能保证它们可以由相同的类加载器加载。如果您完全肯定该代码段仅在使用一个类加载器,则仅使用x.getClass()== SomeFinal.class或其同类。


4
如果一个类是由不同的类加载器加载的,我认为instanceof不会与之匹配。
彼得·劳瑞

0

我也更喜欢枚举方法,但是我将使用抽象基类来强制子类实现该getType()方法。

public abstract class Base
{
  protected enum TYPE
  {
    DERIVED_A, DERIVED_B
  }

  public abstract TYPE getType();

  class DerivedA extends Base
  {
    @Override
    public TYPE getType()
    {
      return TYPE.DERIVED_A;
    }
  }

  class DerivedB extends Base
  {
    @Override
    public TYPE getType()
    {
      return TYPE.DERIVED_B;
    }
  }
}

0

我认为可能值得在此页面上就“ instanceof”不够昂贵而不必担心的普遍共识提出反例。我发现我在内部循环中有一些代码(经过一些历史性的优化尝试)确实

if (!(seq instanceof SingleItem)) {
  seq = seq.head();
}

在SingleItem上调用head()会返回不变的值。通过替换代码

seq = seq.head();

尽管事实上在循环中发生了一些非常繁重的事情,例如字符串到双精度转换,但我还是从269ms加速到169ms。当然,提速的原因可能更多是因为消除了条件分支,而不是消除了instanceof运算符本身。但我认为值得一提。


这可能是由于其if本身。如果trues和falses 的分布接近于偶数,投机执行将变得无用,这将导致严重的滞后。
Dmytro

-4

您正在专注于错误的事情。instanceof和检查同一事物的任何其他方法之间的差异可能甚至无法测量。如果性能至关重要,那么Java可能是错误的语言。主要原因是您无法控制VM何时决定要收集垃圾,这可能会使大型程序中的CPU几秒钟达到100%的运行速度(MagicDraw 10就是这样)。除非您控制着每台计算机,否则都无法运行该程序,因此无法保证它将运行在哪个版本的JVM上,并且许多较旧的版本都存在严重的速度问题。如果它是一个小型应用程序,那么您可以使用Java,但是如果您不断读取和丢弃数据,那么您注意到GC启动的时间。


7
与过去相比,更现代的Java垃圾回收算法的真实性要差得多。即使是最简单的算法也不再关心在使用完之后立即丢弃多少内存,它们只关心在年轻的收藏中保留了多少内存。
比尔·米歇尔

3
太好了,除了我使用的是最新的JVM,并且当GC运行时,我的计算机仍在爬行。在3GB双核ram服务器上。如果性能确实很重要,则Java不是要使用的语言。
08年

@David:您的应用程序消失一段时间后,您不需要实时要求就可以解决问题。我遇到的一个有趣的例子是,一个Java应用程序连接到TCP流,该流在GC运行时就死了,因为它没有先关闭流并且在返回时无法处理网络流量的超载-它会立即进入GC运行的循环,当应用恢复时,它尝试搅动一堆数据,这使它耗尽了内存,从而触发了GC等。Java非常适合很多任务,但不适用于非常重要的任务强大的性能是必要条件。
tloach

6
@tloach在我看来像是不良的应用程序设计。您谈论“性能”就好像是一维的。我已经使用(或开发过)大量的Java应用程序,例如,它们在提供庞大数据集的快速交互式统计分析和可视化方面表现出色,或者在快速处理非常大的事务量方面表现出色。“性能”不仅是一回事,而且有人可以编写糟糕的内存管理应用程序并让GC以自己的方式使用这一事实,并不意味着任何需要“性能”的东西都应该用其他方式编写。
大卫·摩尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.