比instanceof更高效的做法

乌龟壳 发布于 2017/08/08 10:03
阅读 4K+
收藏 1

开源之夏第三届火热来袭,高校学生参与赢万元奖金!>>>

有段代码是判断一些对象是什么类型再产生具体操作的,实际情况有几十个分支

if (o instanceof Rectangle) {
    // do something
} else if (o instanceof Ellipse) {
    // do something
} else if (o instanceof Polygon) {
    // do something
} else {
    // do something
}

但是这样不是性能上面最优的办法。

我知道大家第一反应是做一个接口,让Rectangle/Ellipse/Polygon那些都继承这个接口,实现某些功能,这样根本不用判断

o.doSomething()

确实是性能最优,但是对设计不友好,因为实际情况不单是这一个地方需要判断,如果每个需要判断的地方都新增一个接口函数让类自身去实现,会让这些类代码不断膨胀,而且各种迥异的功能揉在一个类里面也不合理。

所以想咨询下有没有性能上更好的办法去解决这个大量类型判断的问题。

加载中
0
Joyzhou
Joyzhou

没看懂

乌龟壳
乌龟壳
回复 @Joyzhou : 如果只取公共属性,就没必要这么设计了,每个类型都有独特的处理方法,而且在不同的场景下有各自的处理逻辑,而且这些场景随着业务不断开发。
Joyzhou
Joyzhou
回复 @乌龟壳 : 转换到pojo所说的共性应该是某些属性是公共的。instabceof比较太重了。
乌龟壳
乌龟壳
回复 @Joyzhou : 比如这些类都是pojo的场景,这样就好理解了吧
Joyzhou
Joyzhou
回复 @乌龟壳 : 刚才看了下面的回复,实际上我也不理解你所说的“各种迥异的功能”的具体场景。我认为你在一个方法中对于这些对象进行了判断,那么在你的理解中这些对象应该是有共性的,不然就不应该在同一个方法中实现。方法可能是迥异的,但变化的应该只是某些属性而已?
乌龟壳
乌龟壳
修改了问题再看看呢?
0
DeMoNHaDeS
DeMoNHaDeS

1、不让类自身去实现,就要在if-else中实现,代码不也是膨胀吗?

2、既然属于同一种类型,即同一个类,为什么里面糅合的功能会各种迥异,不应该就是这个类的方法吗?

DeMoNHaDeS
DeMoNHaDeS
回复 @乌龟壳 : 我说的一种方案就是将逻辑放到业务逻辑实体中,每个pojo对应一个业务逻辑实体,这样就把二者分离开了。第二种就是你写的这种,或者枚举。性能不是最优,不过响应速度要求不是很高的情况下这些性能损耗不是问题,比网络通讯和sql查询小得多。
乌龟壳
乌龟壳
回复 @DeMoNHaDeS : 没错,你可以理解成这是一种pojo类型的运用,不适合把逻辑也放进去,它只是一个结构。所以问题就是如何比较高效地判断pojo的类型
DeMoNHaDeS
DeMoNHaDeS
回复 @乌龟壳 : 程序是应该更尽量让维护和修改成本更低,但是一些逻辑变动导致的修改是无法避免的。太追求高维护性,反而可能使结构变得更复杂。
DeMoNHaDeS
DeMoNHaDeS
回复 @乌龟壳 : 这个例子感觉更抽象了一些…我的理解。一方面,你的例子更偏向orm中的pojo,但其实你的代码更偏向业务对象,你可以把逻辑放到业务对象里面,pojo和业务对象关联或者有继承关系。另一方面,业务逻辑变了,改model层不是很正常的做法么。就如你的例子,逻辑变了,表还是不变的,逻辑是sql,变得是sql。
乌龟壳
乌龟壳
回复 @DeMoNHaDeS : 举另外一个例子,假设有个数据库的Model层,里面很多class对应数据表,这样就很明显了,不是什么程度的抽象放Model层的。业务逻辑变来变去,不可能每次都要改model层吧
下一页
0
Acce1erator
Acce1erator

接口的设计明明比instanceof更优,然后instanceof操作符经过了高度的优化,我觉得在一般情况下应该不会是性能呢瓶颈吧.

引用Stackoverflow上面的问答:

Q:

The performance impact of using instanceof in Java

I am working on an application and one design approach involves extremely heavy use of the instanceof operator. While I know that OO design generally tries to avoid using instanceof, that is a different story and this question is purely related to performance. I was wondering if there is any performance impact? Is is just as fast as ==?

For example, I have a base class with 10 subclasses. In a single function that takes the base class, I do checks for if the class is an instance of the subclass and carry out some routine.

One of the other ways I thought of solving it was to use a "type id" integer primitive instead, and use a bitmask to represent categories of the subclasses, and then just do a bit mask comparison of the subclasses "type id" to a constant mask representing the category.

Is instanceof somehow optimized by the JVM to be faster than that? I want to stick to Java but the performance of the app is critical. It would be cool if someone that has been down this road before could offer some advice. Am I nitpicking too much or focusing on the wrong thing to optimize?

A:

Modern JVM/JIC compilers have removed the performance hit of most of the traditionally "slow" operations, including instanceof, exception handling, reflection, etc.

As Donald Knuth wrote, "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil." The performance of instanceof probably won't be an issue, so don't waste your time coming up with exotic workarounds until you're sure that's the problem.

乌龟壳
乌龟壳
例如一个user表,做了一个类User,然后业务有十几二十个对User的不同用法,然后全揉在一个User类里?这样很难看好吗。
0
朕百忙之中抽空
朕百忙之中抽空
class XXX{
  public void doSomething(Rectangle r){
    ...
  }
  public void doSomething(Ellipse e){
    ...
  }
  public void doSomething(Polygon p){
    ...
  }
}

 

乌龟壳
乌龟壳
要是代码一开始就清楚具体是什么类型,就不需要instanceof了。比如Rectangle/Ellipse/Polygon都继承Shape,按你给的代码doSomething(Shape)编译器根本不知道转到哪里。
0
乌龟壳
乌龟壳

引用来自“Acce1erator”的评论

接口的设计明明比instanceof更优,然后instanceof操作符经过了高度的优化,我觉得在一般情况下应该不会是性能呢瓶颈吧.

引用Stackoverflow上面的问答:

Q:

The performance impact of using instanceof in Java

I am working on an application and one design approach involves extremely heavy use of the instanceof operator. While I know that OO design generally tries to avoid using instanceof, that is a different story and this question is purely related to performance. I was wondering if there is any performance impact? Is is just as fast as ==?

For example, I have a base class with 10 subclasses. In a single function that takes the base class, I do checks for if the class is an instance of the subclass and carry out some routine.

One of the other ways I thought of solving it was to use a "type id" integer primitive instead, and use a bitmask to represent categories of the subclasses, and then just do a bit mask comparison of the subclasses "type id" to a constant mask representing the category.

Is instanceof somehow optimized by the JVM to be faster than that? I want to stick to Java but the performance of the app is critical. It would be cool if someone that has been down this road before could offer some advice. Am I nitpicking too much or focusing on the wrong thing to optimize?

A:

Modern JVM/JIC compilers have removed the performance hit of most of the traditionally "slow" operations, including instanceof, exception handling, reflection, etc.

As Donald Knuth wrote, "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil." The performance of instanceof probably won't be an issue, so don't waste your time coming up with exotic workarounds until you're sure that's the problem.

这个Stack Overflow的讨论早看过了,没解决我提的问题。

之所以要提这个问题是因为感觉作为一个语言应该有能力提供对这个问题的更高效的解决方案。所以提出问题来看看大家有没有什么这方面的经验。纯粹是讨论这个问题本身。

比如我可否

switch(a.getClass()){

case Rectangle.class:

case Polygon.class:

}

虽然这并非java语法所支持,但是道理上是说得通的,编译期Rectangle.class就能确定了,当然可以组出switch/case的跳转代码。只是目前不支持。

乌龟壳
乌龟壳
回复 @朕百忙之中抽空 : 非常感谢你的建议,不过这个我考虑过了,很不实用,一重构就悲剧了。
朕百忙之中抽空
朕百忙之中抽空
可以用类名的字符串,java7之后switch是支持String的。其实你这个问题和性能优化也没什么关系,更多的是设计模式的问题,if/else和switch的差别也不过是switch大概会有一个内部的索引来映射条件
0
s
someorz

定义业务编码,每一个业务编码对应一个Handler处理类,bussnessCode=XXXXXHandler写到配置文件中,启动时,加载到Map中,调用时,按业务编码从Map中获得Handler处理类。Handler.process(Shape);

乌龟壳
乌龟壳
回复 @someorz : 我说的不实用是不方便重构,至于是if还是map还是swich,因为不方便重构,所以还谈不到那个方面。
s
someorz
回复 @乌龟壳 : 从你现在给的信息中,我只能暂时想到,每一个if判断,就是一个业务编码,没有感觉有什么不妥。每个业务编码有各自的业务处理类,这样更业务上更清楚,用map的方式可以去掉大量的if判断。
乌龟壳
乌龟壳
不是一对一这么简单的问题,写到配置文件再加载到map里,本质就是switch(a.getClass().getName()),前面已经说过这个不实用了。
0
MZHS
MZHS

用一个hashmap搞定,class对象为key,动作实现类为value,一行代码搞定hashmap.get(o.class).do();
动作接口
public interface Operation{

void do();

}

乌龟壳
乌龟壳
回复 @MZHS : 怎样写到最后代码都会乱,map一样。比如你说的这个map,遇到一个人不按套路出牌,在不知道那个乱七八糟的地方对它进行了设置,结果其他人都一脸懵逼也是可能的。
乌龟壳
乌龟壳
回复 @MZHS : 我说的是额外的对象,额外就额外吧,问题是我觉得instanceof就不够快了,你提出的方案不说别的,性能应该没起到提升的坐用
MZHS
MZHS
回复 @乌龟壳 : map的方法虽然类多了点,但是简单清晰,如果都放到if else里或者switch里,随着业务的变迁和多个不同水平人的维护,代码会混乱不堪
MZHS
MZHS
回复 @乌龟壳 : 任何对象创建都需要时间,和空间,即使在一个对象里。如果你不想创建对象那就把实现代码都写到一个类里面,而且还必须去判断o的类型,否则你怎么知道执行哪段代码?
乌龟壳
乌龟壳
这个方法我也考虑过。1.hashmap本身的构造需要时间,2.每个class都要额外对应至少一个实例化的匿名类等(看实现方式),这样算下来整体的效果并不好。
0
朕百忙之中抽空
朕百忙之中抽空

翻源码的时候无意中发现有类似的情况,Arrays.deepEquals(Object[] a1,Object[] a2),里面的deepEquals0方法,简直和楼主的最初版代码一毛一样。所以说instanceof没毛病,if/else也没那么不堪,搞成switch或者hashmap无非相当于加了索引,硬要分析性能的话,无非是减少了时间复杂度增加了空间复杂度。不知道楼主要做怎样的重构,可是无论如何,每个doSomething()你要重新写终归还是要重新写。

OSCHINA
登录后可查看更多优质内容
返回顶部
顶部
返回顶部
顶部