Вопрос по performance, java – Влияние на производительность использования instanceof в Java

283

Я работаю над приложением, и один из подходов к проектированию предполагает чрезвычайно интенсивное использованиеinstanceof оператор. Хотя я знаю, что ОО дизайн вообще старается избегать использованияinstanceof, это другая история, и этот вопрос связан исключительно с производительностью. Мне было интересно, есть ли какое-либо влияние на производительность? Это так же быстро, как==?

Например, у меня есть базовый класс с 10 подклассами. В единственной функции, которая принимает базовый класс, я проверяю, является ли класс экземпляром подкласса, и выполняю некоторую процедуру.

Один из других способов решения этой проблемы - использовать вместо этого целочисленный примитив «id типа» и использовать битовую маску для представления категорий подклассов, а затем просто выполнить сравнение битовой маски для подклассов «id типа» для постоянная маска, представляющая категорию.

Являетсяinstanceof каким-то образом оптимизирована JVM, чтобы быть быстрее, чем это? Я хочу придерживаться Java, но производительность приложения имеет решающее значение. Было бы здорово, если бы кто-то, кто был на этом пути раньше, мог бы дать совет. Я слишком много придираюсь к чему-то или фокусируюсь не на том, что нужно оптимизировать?

Производительность instanceof и кастинга довольно хорошие. Я опубликовал некоторые моменты в Java7 вокруг различных подходов к проблеме здесь: / Stackoverflow.com вопросы / 16320014 / ... Wheezil
Тьфу ... Почему все ответы не соответствуют сути вопроса и содержат одну и ту же старую риторику Кнута об оптимизации? Ваш вопрос о том, является ли instanceof значительно / на удивление медленнее, чем проверка объекта класса с помощью ==, и я обнаружил, что это не так. gubby
Я думаю, что смысл вопроса, однако, заключался в том, чтобы отложить вопрос о наилучшей ОО-практике и изучить эффективность. Dave L.
@ Дэйв Л. Обычно я бы согласился, но ОП упоминает, что он ищет некоторые общие методы оптимизации, и он не уверен, связана ли его проблема с «instanceof». Я думаю, что стоит хотя бы упомянуть «правильный» дизайн, чтобы он мог профилировать оба варианта. Outlaw Programmer

Ваш Ответ

23   ответа
247

и большинства традиционно «медленных» операций, включая instanceof, обработку исключений, отражение и т. Д.

Как писал Дональд Кнут: «Мы должны забыть о малой эффективности, скажем, в 97% случаев: преждевременная оптимизация - корень всего зла». Производительность instanceof, вероятно, не будет проблемой, поэтому не тратьте свое время на экзотические обходные пути, пока не убедитесь, что это проблема.

Современная JVM / JIC. Не могли бы вы упомянуть, с какой версии Java была проведена оптимизация? Ravisha
Всегда есть кто-то, кто цитирует Кнута, когда производительность является темой ... Забывая, что Кнут также заявил (в той же статье): «В устоявшихся инженерных дисциплинах 12% -ное улучшение, легко достижимое, никогда не считается незначительным, и я верю в то же самое точка зрения должна преобладать в разработке программного обеспечения ", почти вся его работа была посвящена эффективности алгоритмов, и он писал алгоритмы на ассемблере, чтобы (среди прочего) добиться лучшей производительности. Мех ... kgadek
А здесь, но будетtry { ObjT o = (ObjT)object } catch (e) { no not one of these } будет медленнее быстрее ?? peterk
Если «объект» является экземпляром ObjT, приведение его происходит немного быстрее, чем выполнение instanceof, но разница, которую обнаружил мой быстрый тест, составляла 10–20 мс за 10 000 000 итераций. Если «объект» не является ObjT, то перехват исключения происходил более чем в 3000 раз медленнее - более 31 000 мс против ~ 10 мс для instanceof. Steve
такой сильный аргумент без какой-либо «ссылки», совершенно бесполезен, потому что просто самоуверен. marcorossi
13

но поскольку я не нашел «метрики производительности» для варианта использования, аналогичного моему, я сделал еще несколько примеров кода. На моем оборудовании и Java 6 & 7 разница между instanceof и переключением на 10 миллионов итераций равна

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? Более важно то, что вы сравниваете длину if instanceofs с тем, что важно в выражении case для целых чисел. Я думаю, что мы ожидаем, что Int-переключатель будет светиться быстро. Azeroth2b
Я не могу точно вспомнить, что происходило 5 лет назад - я думаю, что и у Java 6, и у Java 7 был похожий результат, поэтому предоставляется только один результат (при условии, что 2 строки для различной глубины иерархии классов) ... и нет, я не пробовал сравнивать с Java 8. Весь код теста предоставлен - вы можете скопировать / вставить его и проверить в нужных вам средах (обратите внимание - в настоящее время я бы использовал тест JMH для этого). Xtra Coder
234
Подходит

эталонная программа для оценки различных реализаций:

instanceof реализация (как ссылка) объект ориентирован через абстрактный класс и@Override метод испытания использование собственной реализации типаgetClass() == _.class реализаци

Я использовал JMH для запуска теста с 100 разминочными вызовами, 1000 итерациями при измерении и 10 разветвлениями. Таким образом, каждая опция была измерена в 10 000 раз, что требует 12:18:57 для запуска целого теста на моем MacBook Pro с macOS 10.12.4 и Java 1.8. Тест измеряет среднее время каждого варианта. Для более подробной информации смотрите моя реализация на GitHub.

Ради полноты: есть предыдущая версия этого ответа и мой тест.

Полученные результат
| Operation  | Runtime in nanoseconds per operation | Relative to instanceof |
|------------|--------------------------------------|------------------------|
| 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; др

В Java 1.8instanceof - самый быстрый подход, хотяgetClass() очень близко.

+0.(9) для науки vaxquis
+ остальные 0.1 от меня: D Tobias Reich
@ TobiasReich Итак, мы получили+1.0(9). :) Pavel
Или просто делай хронометраж всего миллиарда звонков, а не за звонок. LegendLength
Я не думаю, что это вообще что-то значит. Код измеряет с помощьюSystem.currentTimeMillis() для операции, которая не намного больше, чем один вызов метода, который должен давать большую точность. Используйте тестовую среду, такую как JMH вместо этого! Lii
72

чтобы увидеть, как производительность instanceOf сравнивается с простым вызовом s.equals () строкового объекта с одной буквой.

В 10.000.000 цикле instanceOf дал мне 63-96мс, а строка равных - 106-230мс

Я использовал java jvm 6.

Так что в моем простом тесте быстрее сделать instanceOf вместо сравнения строк из одного символа.

using Integer .equals () вместо строк дал мне тот же результат, только когда я использовал == я был быстрее, чем instanceOf на 20 мс (в цикле 10.000.000)

Как сравнить instanceOf с отправкой полиморфной функции? Chris
@ marsbearequals() не будет сокращать, потому что подклассы; тебе нужноisAssignableFrom(). David Moles
@ marsbear Правильно, но это не лучший тест того, о чем спрашивал ОП. David Moles
Почему вы сравниваете instanceof со String.equals ()? Если вы хотите проверить тип, который вам нужно для object.getClass (). Equals (SomeType.class) marsbear
Можно ли разместить здесь код? Это было бы круто The Alchemist
8

instanceof очень быстро, требует всего несколько инструкций процессора.

Очевидно, если классX не имеет загруженных подклассов (JVM знает),instanceof можно оптимизировать как:

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

Основная стоимость - только чтение!

ЕслиX имеет загруженные подклассы, требуется еще несколько чтений; они, вероятно, расположены рядом, поэтому дополнительные расходы тоже очень низкие.

Хорошие новости всем!

може быть оптимизированным илиявляетс оптимизирован? источник vaxquis
@ vaxquisможе как его JVM подразумевает специфический RecursiveExceptionException
@ itzJanuaryвздо Вы упустили смысл моего вопроса здесь: все знают этот компиляторможе оптимизироватьfoo - ноявляетсяfoo на самом деле в настоящее время оптимизирован под Oracle javac / VM - или просто возможно, что он сделает это в будущем? Кроме того, я спросил ответчика имеет ли он какой-либо вспомогательный источник (будь то документы, исходный код, блог разработчика), документирующий, что он действительно может быть оптимизирован или оптимизирован? Без этого этот ответ - просто случайное размышление о том, какой компиляторможе возможно сделаю. vaxquis
@ vaxquis Вы никогда не упоминали виртуальную машину Hotspot, но в этом случае я не знаю, оптимизирована ли она. RecursiveExceptionException
Недавно прочитайте, что JIT (JVM 8) оптимизирует сайт вызовов для 1 или 2 типов путем прямых вызовов, но возвращается к vtable, если встречаются более двух реальных типов. Таким образом, только два конкретных типа, проходящих через сайт вызова во время выполнения, представляют собой преимущество в производительности. simon.watts
4

поэтому ваша производительность вряд ли пострадает. Однако использование большого количества instanceof наводит на мысль о проблеме дизайна.

Если вы можете использовать xClass == String.class, это быстрее. Примечание: вам не нужен instanceof для финальных классов.

Кстати, что ты имеешь в виду под «не нужен instanceof для выпускных классов»? Pacerier
Последний класс не может иметь подклассов. В этом случаеx.getClass() == Class.class такой же какx instanceof Class Peter Lawrey
Cool, при условии, что x не нуль, что бы вы предпочли? Pacerier
Хорошая точка зрения. Это будет зависеть от того, ожидаю ли яx бытьnull Я предполагаю. (Или что будет яснее) Peter Lawrey
Ай, спасибо за помощь = D Pacerier
17

Количество возможных классов, для которых оператор instanceof мог бы вернуть true Распределение ваших данных - разрешены ли большинство операций instanceof с первой или второй попытки? Сначала вы захотите вернуть свои наиболее вероятные операции. Среда развертывания. Работа на виртуальной машине Sun Solaris существенно отличается от виртуальной виртуальной машины Sun Windows. По умолчанию Solaris будет работать в режиме «сервер», а Windows - в режиме клиента. Оптимизация JIT в Solaris сделает доступ ко всем методам одинаковым.

Я создалmicrobenchmark для четырех разных способов отправки. Результаты Solaris следующие: меньшее число быстрее:

InstanceOf 3156
class== 2925 
OO 3083 
Id 3067 
16

если профайлер не скажет вам, что вы тратите смешное количество времени в экземпляре: да, вы придирчивы.

Прежде чем задуматься об оптимизации чего-то, что никогда не нужно было оптимизировать: напишите свой алгоритм наиболее читабельным способом и запустите его. Запускайте его, пока jit-компилятор не получит возможность оптимизировать его самому. Если у вас возникли проблемы с этим фрагментом кода, используйте профилировщик, чтобы сообщить, где получить максимальную выгоду и оптимизировать это.

Во времена высокооптимизирующих компиляторов ваши предположения о узких местах, скорее всего, будут полностью неверными.

И в истинном духе этого ответа (которому я искренне верю): я абсолютно не знаю, как соотносятся instanceof и ==, как только jit-компилятор получает возможность оптимизировать его.

Я забыл: никогда не измеряй первый прогон.

GWBasic может стать отличным началом для 3D-игр, если есть подходящие библиотеки. Но это в стороне (поскольку это искусственный аргумент): OP не требует полного переписывания в качестве оптимизации. Речь идет об одной конструкции, в которой мы даже не знаем, является ли влияние значительным (даже если есть более эффективный способ сделать то же самое в текущей версии компилятора). Я твердо стою позади C2.com / CGI / вики? ProfileBeforeOptimizing и мой ответ. Предварительная оптимизация - корень всего зла! Это усложняет обслуживание - и это тот аспект, который стоит оптимизироват Olaf Kock
ли профилировщики профиля встроенные операторы? Seun Osewa
Но оригинальный постер упоминал, что производительность была критически важной для этого приложения, поэтому оптимизацию на ранних этапах в этой ситуации не исключено. Другими словами, вы не написали бы 3d-игру на GWBasic, а потом в конце сказали: хорошо, давайте начнем оптимизировать это, первый шаг - перенести ее на c ++. LegendLength
5

вероятно, будет более дорогостоящим, чем простое равенство, в большинстве реализаций реального мира (то есть тех, где действительно требуется , и вы не можете просто решить эту проблему, переопределив общий метод, как и любой учебник для начинающих, а также как предлагает Демьян выше).

Почему это? Потому что, вероятно, произойдет то, что у вас есть несколько интерфейсов, которые предоставляют некоторую функциональность (скажем, интерфейсы x, y и z) и некоторые объекты для манипулирования, которые могут (или нет) реализовывать один из этих интерфейсов ... но не напрямую. Скажем, например, у меня есть:

w расширяет х

A реализует w

B расширяет A

C расширяет B, реализует y

D расширяет C, реализует z

Предположим, я обрабатываю экземпляр D, объект d. Для вычислений (d x) требуется взять d.getClass (), пройтись по циклам через интерфейсы, которые он реализует, чтобы узнать, равен ли один == x, и если нет, сделать это снова рекурсивно для всех их предков ... В нашем случае, если вы в первый раз исследуете это дерево, вы получите как минимум 8 сравнений, предположив, что y и z ничего не расширяют ...

Сложность дерева деривации в реальном мире, вероятно, будет выше. В некоторых случаях JIT может оптимизировать большую часть его, если он может заранее разрешить d как во всех возможных случаях экземпляр чего-то, что расширяет x. Реально, однако, вы будете проходить этот обход дерева большую часть времени.

Если это станет проблемой, я бы предложил вместо этого использовать карту обработчика, связав конкретный класс объекта с замыканием, которое выполняет обработку. Это удаляет фазу обхода дерева в пользу прямого отображения. Однако помните, что если вы установили обработчик для C.class, мой объект d выше не будет распознан.

Вот мои 2 цента, надеюсь, они помогут ...

4

как + или -, и я считаю, что у него есть собственная инструкция байт-кода JVM. Это должно быть достаточно быстро.

Мне не следует, что если у вас есть переключатель, на котором вы тестируете, является ли объект экземпляром некоторого подкласса, то ваш дизайн, возможно, придется переделать. Подумайте о том, чтобы внедрить специфичное для подкласса поведение в сами подкласс

4

который используется для сравнения ссылок на классы. Попробуйте несколько миллионов instanceofs в цикле и убедитесь сами.

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

Экземпля является предупреждением о плохом объектно-ориентированном дизайне.

Текущие JVM означаэкземпляам по себе @ не очень беспокоит производительность. Если вы часто используете его, особенно для основной функциональности, возможно, пришло время взглянуть на дизайн. Прирост производительности (и простота / ремонтопригодность) от рефакторинга к лучшему дизайну значительно перевесит любые фактические циклы процессора, потраченные на фактическийэкземпля вызов

Дать очень маленький пример упрощенного программирования.

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

В случае плохой архитектуры лучшим выбором было бы, чтобы SomeObject был родительским классом двух дочерних классов, где каждый дочерний класс переопределяет метод (doSomething), поэтому код будет выглядеть так:

Someobject.doSomething();
Я знаю об этом. Это было не то, что я спросил. Josh
Не уверен, стоит ли голосовать за это или нет, потому что это хороший вопрос, но он не отвечает на заданный вопрос ... jklp
Я думаю, что пример кода на самом деле очень плохой: вы не можете расширить класс Double, а также вы не можете получить Double из какого-то другого класса. Если бы вы использовали другие классы для примера, все было бы хорошо. Lena Schimmel
Также, если дочерние классы SomeObject являются объектами-значениями, вы не хотите помещать в них логику. Например. Pie and Roast может быть неправильным местом для логики putInOven () и putInMouth (). sk.
Сам приготовить пирог и жаркое было бы здорово, хотя binboavetonik
3

как конкретная JVM реализует экземпляр, но в большинстве случаев Объекты сравнимы со структурами, а также с классами, и каждая структура объекта имеет указатель на структуру класса, экземпляром которой он является. Так на самом деле instanceof для

if (o instanceof java.lang.String)

может быть так же быстро, как следующий код C

if (objectStruct->iAmInstanceOf == &java_lang_String_class)

предполагая, что JIT-компилятор работает и работает достойно.

Считая, что это только доступ к указателю, получение указателя с определенным смещением, на которое указывает указатель, и сравнение его с другим указателем (что в основном совпадает с проверкой на равенство 32-битных чисел), я бы сказал, что операция может на самом деле очень быстро.

Это не обязательно, хотя это во многом зависит от JVM. Однако, если это окажется узким местом в вашем коде, я бы посчитал реализацию JVM довольно плохой. Даже тот, у которого нет JIT-компилятора и только интерпретирует код, должен иметь возможность сделать экземпляр теста практически мгновенно.

Разве не нужно выяснить, наследуется ли o от java.lang.String? WW.
Вот почему я сказал, что это может быть так же быстро. На самом деле он выполняет цикл, сначала проверяя iAmInstanceOf на предмет соответствующего класса, затем поднимается вверх по дереву наследования o и повторяет эту проверку для каждого суперкласса o (поэтому может потребоваться выполнить этот цикл пару раз. на матч) Mecki
3

ли ее отсутствия) вообще состоит в том, чтобы создать родительский интерфейс для всех подклассов, на которых вам нужно сделать instanceof. Интерфейс будет супер наборомвс методы в подклассах, для которых вам нужно сделать экземпляр проверки. Если метод не применяется к конкретному подклассу, просто предоставьте фиктивную реализацию этого метода. Если я не понял проблему неправильно, то вот как я обошел проблему в прошлом.

2

причина, по которой оператор instanceof вызывает неодобрение в подобном случае (где instanceof проверяет подклассы этого базового класса), заключается в том, что вам нужно переместить операции в метод и переопределить его для соответствующие подклассы. Например, если у вас есть:

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

Вы можете заменить это на

o.doEverything();

и затем иметь реализацию doEverything () в вызове Class1 «doThis ()», а в Class2 вызов «doThat ()» и т.

Но иногда ты не можешь. Если вы реализуете интерфейс, в котором вы берете объект, и вам нужно сказать, какой это тип, то instanceof действительно единственный вариант. Вы можете попробовать кастинг, но instanceof обычно чище. Herms
2

чем простой вызов метода. Это означает

if(a instanceof AnyObject){
}

быстрее как:

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

Еще одна вещь, если вам нужно каскадировать много экземпляров. Тогда переключатель, который вызывает только один раз 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;
}

terrible OO design, но если ваш анализ производительности показывает, что это то, где вы узкое место, то, возможно,. В моем коде код отправки занимает 10% от общего времени выполнения, и это может способствовать повышению общей скорости на 1%.

0

если это действительно проблема производительности в вашем проекте. Если это так, я бы порекомендовал редизайн - если это возможно. Я почти уверен, что вы не можете превзойти нативную реализацию платформы (написано на C). В этом случае также следует учитывать множественное наследование.

Вы должны рассказать больше о проблеме, может быть, вы могли бы использовать ассоциативный магазин, например, Карта <Class, Object>, если вас интересуют только конкретные типы.

0

что вам не нужен instanceof для выпускных классов и вы можете просто использовать равенство ссылок, будьте осторожны! Хотя конечные классы не могут быть расширены, они не гарантированно загружаются одним и тем же загрузчиком классов. Используйте x.getClass () == SomeFinal.class или тому подобное, только если вы абсолютно уверены, что для этой части кода в игре только один загрузчик классов.

Если класс загружается другим загрузчиком классов, я не думаю, что instanceof тоже подойдет. Peter Lawrey
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 = se,q.head();
}

where вызов head () для SingleItem возвращает значение без изменений. Замена кода на

seq = seq.head();

дает мне ускорение с 269 мс до 169 мс, несмотря на то, что в цикле происходят довольно тяжелые вещи, такие как преобразование строки в двойное. Конечно, возможно, что ускорение происходит больше за счет устранения условного перехода, чем за счет исключения самого оператора; но я думал, что стоит упомянуть.

Это может быть из-заif сам. Если распределениеtrues иfalses близок к четному, спекулятивное исполнение становится бесполезным, что приводит к значительным задержкам. Dmitriy
-3

тодом проверки того же самого, вероятно, даже не поддается измерению. Если производительность критична, то Java, вероятно, не тот язык. Основная причина в том, что вы не можете контролировать, когда виртуальная машина решает, что она хочет собирать мусор, что может привести к увеличению загрузки ЦП до 100% в течение нескольких секунд в большой программе (MagicDraw 10 отлично подходит для этого). Если вы не контролируете каждый компьютер, на котором будет работать эта программа, вы не сможете гарантировать, на какой версии JVM она будет работать, и у многих из старых были серьезные проблемы со скоростью. Если это небольшое приложение, вы можете быть в порядке с Java, но если вы постоянно читаете и отбрасываете данные, то вывол заметьте, когда сработает GC.

Это гораздо менее верно в отношении более современных алгоритмов сборки мусора Java, чем когда-либо раньше. Даже самые простые алгоритмы больше не заботятся о том, сколько памяти вы выбрасываете сразу после ее использования - они заботятся только о том, сколько осталось в коллекциях молодого поколения. Bill Michell
Отлично, за исключением того, что я использую последнюю версию JVM, и мой компьютер все еще сканирует во время работы GC. На двухъядерном оперативном сервере 3 ГБ. Java не является языком, который нужно использовать, если производительность действительно имеет значение. tloach
Реальное время - это не то же самое, что исполнитель. David Moles
@ David: Вам не нужно требовать, чтобы в режиме реального времени возникали проблемы, когда ваше приложение исчезало в течение определенного периода времени. Самое интересное, с чем я столкнулся, это приложение Java, которое подключалось к потоку TCP, который умер, когда GC работал, потому что он не закрывал поток первым и не мог справиться с перегрузкой сетевого трафика, когда он вернулся - он немедленно войти в цикл, в котором запускается GC, когда приложение возобновляет работу, оно пытается перетаскивать кучу данных, что приводит к нехватке памяти, что вызывает GC и т. д. Java отлично подходит для множества задач, но не для задач, где высокая производительность является требованием. tloach
@ tloach для меня звучит как плохой дизайн приложения. Вы говорите о «производительности», как если бы она была одномерной. Я работал с множеством java-приложений (и над ними), которые, например, были эффективны в обеспечении быстрого интерактивного статистического анализа и визуализации очень больших наборов данных или очень быстро обрабатывали очень большие объемы транзакций. «производительность» - это не одно, а тот факт, что кто-то может написать приложение, которое плохо управляет памятью и позволяет GC работать по-своему, не означает, что что-то, требующее «производительности», должно быть написано в другом мест David Moles

Похожие вопросы