Объявление переменной внутри цикла - PullRequest
8 голосов
/ 05 января 2010

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

List list=myObject.getList();
Iterator itr=list.iterator();
while (itr.hasNext()){
    BusinessObject myBo=(BusinessObject)itr.next();
    process(myBo);
}

В приведенном выше фрагменте, должно ли myBo быть объявлено вне цикла или его объявление внутри цикла не наносит вреда памяти и производительности?

Ответы [ 9 ]

8 голосов
/ 05 января 2010

Объявление его внутри цикла не нанесет вреда памяти и производительности.

5 голосов
/ 05 января 2010

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

Таким образом, делать то, что вы делаете, считается ХОРОШИМ.

5 голосов
/ 05 января 2010

Если возможно, используйте List<BusinessObject> и Iterator<BusinessObject>, чтобы избежать приведения:

List<BusinessObject> list = myObject.getList();
Iterator<BusinessObject> itr = list.iterator();

while (itr.hasNext()) {
   process(itr.next());
}
4 голосов
/ 05 января 2010

Наиболее элегантным решением для вашего цикла будет расширенный цикл for (Java 5 или новее):

List<BusinessObject> list = myObject.getList();

for( BusinessObject myBo : list ) {
    process(myBo);
}

Но даже с предоставленным вами кодом проблем с производительностью не возникнет, поскольку все временные переменные содержат только ссылки на BusinessObject , что очень дешево.

4 голосов
/ 05 января 2010

myBo - это просто ссылка на объект (который возвращается itr.next ()). Таким образом, объем необходимой памяти очень мал и создается только один раз, и добавление его в цикл не должно влиять на вашу программу. IMO, объявление его внутри цикла, где он используется, на самом деле помогает сделать его более читабельным.

2 голосов
/ 05 января 2010

Не наносит вреда памяти.

Кстати, если вы не пропустите какой-либо код, вы можете вообще пропустить объявление:

while (itr.hasNext()){
    //BusinessObject myBo=(BusinessObject)itr.next();
    process((BusinessObject)itr.next());
} 
1 голос
/ 11 сентября 2012

Просто взгляните на байт-код с javap -c [ClassName]. Вот класс, демонстрирующий несколько примеров одноразовых переменных с циклами. Соответствующий дамп байт-кода находится в комментариях:

class HelloWorldLoopsAnnotated {
    //
    // HelloWorldLoopsAnnotated();
    //   Code:
    //    0:   aload_0
    //    1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
    //    4:   return
    ////////////////////////////////////////////////////////////////////////////

    void stringDeclaredInsideLoop(){
        while (true) {
            // 0:   ldc #2; //String Hello World!
            String greeting = "Hello World!";
            doNothing(greeting);
        }
    }
    //
    // void stringDeclaredInsideLoop();
    //   Code:
    //    0:   ldc #2; //String Hello World!
    //    2:   astore_1
    //    3:   aload_0
    //    4:   aload_1
    //    5:   invokespecial   #3; //Method doNothing:(Ljava/lang/String;)V
    //    8:   goto    0
    ////////////////////////////////////////////////////////////////////////////

    void stringDeclaredOutsideLoop(){
        String greeting;
        while (true) {
            greeting = "Hello World!";
            doNothing(greeting);
        }
    }
    //
    // void stringDeclaredOutsideLoop();
    //   Code:
    //    0:   ldc #2; //String Hello World!
    //    2:   astore_1
    //    3:   aload_0
    //    4:   aload_1
    //    5:   invokespecial   #3; //Method doNothing:(Ljava/lang/String;)V
    //    8:   goto    0
    ////////////////////////////////////////////////////////////////////////////

    void stringAsDirectArgument(){
        while (true) {
            doNothing("Hello World!");
        }
    }
    // void stringAsDirectArgument();
    //   Code:
    //    0:   aload_0
    //    1:   ldc #2; //String Hello World!
    //    3:   invokespecial   #3; //Method doNothing:(Ljava/lang/String;)V
    //    6:   goto    0
    ////////////////////////////////////////////////////////////////////////////

    private void doNothing(String s) {
    }
}

stringDeclaredInsideLoop() и stringDeclaredOutsideLoop() дают идентичный байт-код из шести инструкций. stringDeclaredInsideLoop() все еще выигрывает: ограниченная область действия лучше .

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

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

Подробнее см. .

1 голос
/ 05 января 2010

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

0 голосов
/ 05 января 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...