It turns out that instanceof is faster than the visitor pattern presented above; I think this should question us if the visitor pattern is more elegant than instanceof when it does the same slower with more lines of code?
Here is my test. I compared 3 methods: the visitor pattern above, instanceof, and the explicit type field in Animal.
OS: Windows 7 Enterprise SP1, 64-bit
Processor: Intel (R) Core (TM) i7 CPU 860 @ 2.80 GHz 2.93 GHz
RAM: 8.00 GB
JRE: 1.7.0_21-b11, 32-bit
import java.util.ArrayList; import java.util.List; public class AnimalTest1 { public static void main(String[] args) { Animal lion = new Lion("Geo"); Animal deer1 = new Deer("D1"); Animal deer2 = new Deer("D2"); List<Animal> li = new ArrayList<Animal>(); li.add(lion); li.add(deer1); li.add(deer2); int reps = 10000000; long start, elapsed; start = System.nanoTime(); for (int i = 0; i < reps; i++) { for (Animal a : li) a.accept(new ActionVisitor()); // <-- Accept / visit. } elapsed = System.nanoTime() - start; System.out.println("Visitor took " + elapsed + " ns"); start = System.nanoTime(); for (int i = 0; i < reps; i++) { for (Animal a : li) { if (a instanceof Lion) { ((Lion) a).roar(); } else if (a instanceof Deer) { ((Deer) a).runAway(); } } } elapsed = System.nanoTime() - start; System.out.println("instanceof took " + elapsed + " ns"); start = System.nanoTime(); for (int i = 0; i < reps; i++) { for (Animal a : li) { switch (a.type) { case Animal.LION_TYPE: ((Lion) a).roar(); break; case Animal.DEER_TYPE: ((Deer) a).runAway(); break; } } } elapsed = System.nanoTime() - start; System.out.println("type constant took " + elapsed + " ns"); } } abstract class Animal { public static final int LION_TYPE = 0; public static final int DEER_TYPE = 1; String name; public final int type; public Animal(String name, int type) { this.name = name; this.type = type; } public abstract void accept(AnimalVisitor av); // <-- Open up for visitors. } class Lion extends Animal { public Lion(String name) { super(name, LION_TYPE); } public void roar() { // System.out.println("Roar"); } public void accept(AnimalVisitor av) { av.visit(this); // <-- Accept and call visit. } } class Deer extends Animal { public Deer(String name) { super(name, DEER_TYPE); } public void runAway() { // System.out.println("Running..."); } public void accept(AnimalVisitor av) { av.visit(this); // <-- Accept and call visit. } } interface AnimalVisitor { void visit(Lion l); void visit(Deer d); } class ActionVisitor implements AnimalVisitor { public void visit(Deer d) { d.runAway(); } public void visit(Lion l) { l.roar(); } }
Test results:
The visitor accepted 920842192 ns
instanceof took 511837398 ns
type of constant took 535296640 ns
This visitor pattern introduces 2 additional method calls that are not needed with instanceof. This is probably why it is slower.
Not that performance is the only consideration, but pay attention to how 2 instances are faster than even a switch statement with two cases. A lot of people were worried about instance performance, but that should fix the concern.
As a Java developer, I feel frustrated when people have a dogmatic attitude about avoiding using instanceof, because there were several times in my work, I wanted to clear or write new clean code using instanceof, but the staff / superiors did not approve this approach because they more or less blindly accepted the idea that instanceof should never be used. I feel upset because this moment often leads home with examples of toys that do not reflect the real problems of the business.
Whenever you do modular software development, there will always be times when type-specific decisions need to be isolated from their respective types so that types have as few dependencies as possible.
This visitor pattern does not violate modularity, but is not an excellent alternative to instanceof.
Andy May 7 '13 at 16:08 2013-05-07 16:08
source share