Как Java обрабатывает использование геттера в цикле for? - PullRequest
0 голосов
/ 22 октября 2018

Например, если у меня есть нижеследующее для объявления цикла.

for(Foo f : fooService.getFooList()){}

Более эффективно сначала вызвать геттер и присвоить его временному List<Foo> fooList = fooService.getFooList(); илинормально использовать встроенный метод получения?

Ответы [ 4 ]

0 голосов
/ 22 октября 2018

Если вас беспокоит то, что fooService.getFooList() будет вызываться n раз для получения каждого значения f, игнорируйте эту мысль.Это вызывает getFooList() один раз и перебирает его результат.

Например, в следующем фрагменте :

class Ideone
{
    private static List<String> list = Arrays.asList("A", "B", "C");

    public static void main (String[] args) throws java.lang.Exception
    {
        for(String f : getFooList()){
           System.out.println(f);
        }
    }

    private static List<String> getFooList() {
        System.out.println("getFooList called");
        return list;
    }
}

getFooList called печатается только один раз, что указывает на то, чтометод getFooList() вызывается циклом for только один раз.После этого печатается A, B, C, итерация по элементам, полученным из вызова метода.

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

0 голосов
/ 22 октября 2018

В этом конкретном случае, с циклом foreach, я ожидаю, что ваш код скомпилируется примерно так:

Iterator<Foo> it = fooService.getFooList().iterator();
while (it.hasNext()) {
    Foo foo = it.next();
    // ...
}

Так что в этом случае это не имеет значения.

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

for(int i = 0; i < fooService.getFooList().size(); i++){
    Foo foo = fooService.getFooList().get(i);
    // ...
}

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

Но учтите, что если размер списка изменяется, то i < fooService.getFooList().size() отражает это.Это может быть полезно, если вы знаете об этом, но опасно, если вы этого не делаете.

Если вы знаете, что размер списка не изменится, вы можете сделать что-то подобное, чтобы устранить эти издержки (или просто использовать Iterator или улучшенный цикл for-each, если вам не нужны индексы):

List<Foo> fooList = fooService.getFooList();
final int fooListSize = fooList.size();
for(int i = 0; i < fooListSize ; i++){
    Foo foo = fooList.get(i);
    // ...
}

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

Но если ваше приложениезависит от времени выполнения, и ваш список велик, и эти небольшие проверки складываются, тогда вы можете захотеть сделать что-то подобное выше.

0 голосов
/ 22 октября 2018

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

К вашему сведению: за сценой List не будет использоваться сам по себе, но его Iterator.

Таким образом, ваш цикл for можно сформулировать так:

for (Iterator<Foo> i = fooService.getFooList(); i.hasNext();) {
    Foo foo= i.next();
}

См. языковые спецификации о for петлях для получения дополнительной информации

0 голосов
/ 22 октября 2018

Неэффективнее сначала назначить ссылку на Список, но наличие ссылки может быть полезным, в зависимости от того, что вы пытаетесь сделать.

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