Что происходит под капотом расширенного цикла? - PullRequest
0 голосов
/ 11 июня 2018

Я написал следующий тест при изучении Java расширенного цикла for:

class test
{
    int number = 0;

    public static void main(String args[]) {
        new test();
    }

    public test() {

        int[] numbers = getNumbers();

        for(int number : numbers) {
            System.out.println("number    : " + number);
            System.out.println("numbers[0]: " + numbers[0]);

            numbers = getNumbers();
        }
    }

    public int[] getNumbers() {

        number++;

        int[] numbers = new int[5];

        for(int i = 0; i < numbers.length; i++)
            numbers[i] = number;

        return numbers;
    }
}

Я был удивлен, обнаружив, что мой тест вышел:

number    : 1
numbers[0]: 1
number    : 1
numbers[0]: 2
number    : 1
numbers[0]: 3
number    : 1
numbers[0]: 4
number    : 1
numbers[0]: 5

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

Я подтвердил, что поведение по крайней мере похоже на это, заменив цикл на:

for(int number : numbers) {
    System.out.println("number    : " + number);
    numbers = null;
}

, который дал:

number    : 1
number    : 1
number    : 1
number    : 1
number    : 1

Мой вопрос: чтоповедение скрыто от нас здесь?Вторая версия numbers скопирована?Это на самом деле неизменный, или я не ткнул его достаточно сильно?Другими словами:

Что происходит под капотом расширенного цикла for?

Ответы [ 5 ]

0 голосов
/ 12 июня 2018

Из документов Java

"Например, этот код:

List<? extends Integer> l = ...
for (float i : l) ...

будет переведен в:

for (Iterator<Integer> #i = l.iterator(); #i.hasNext(); ) {
    float #i0 = (Integer)#i.next();
    ...

https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.14.2

Так что да, ваши наблюдения верны!

0 голосов
/ 12 июня 2018

В кавычках JLS :

Расширенный оператор for эквивалентен базовому for выражению в форме:

T[] #a = Expression;
L1: L2: ... Lm:
for (int #i = 0; #i < #a.length; #i++) {
    {VariableModifier} TargetType Identifier = #a[#i];
    Statement
}

Обратите внимание, что Expression оценивается только один раз.

0 голосов
/ 11 июня 2018

Похоже, что создается отдельный экземпляр чисел ...

Ну ... да.Вы создали пять новых экземпляров, запустив new int[5] 6 раз.И цикл for продолжает использовать первый.Первый не собирает мусор до тех пор, пока не закончится цикл for, даже если вы больше не можете ссылаться на него.

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

0 голосов
/ 11 июня 2018

Усовершенствованная петля for покрыта JLS, раздел 14.14.2 :

  • Для Iterable объектов:

Расширенный оператор for эквивалентен базовому оператору for в форме:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
    {VariableModifier} TargetType Identifier =
        (TargetType) #i.next();
    Statement
}
  • Для массивов:

Расширенный оператор for эквивалентен базовому оператору for в форме:

T[] #a = Expression;
L1: L2: ... Lm:
for (int #i = 0; #i < #a.length; #i++) {
    {VariableModifier} TargetType Identifier = #a[#i];
    Statement
}

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

В течение Iterable s, сохраняется только ссылка на Iterator,который по-прежнему ссылается на исходный объект Iterable, даже если вы переназначаете исходную переменную во время цикла.

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

0 голосов
/ 11 июня 2018

В строке вашего расширенного цикла for следующим образом:

    for(int number : numbers) {

любое выражение, которое следует после :, оценивается только один раз : при запуске цикла for.Даже если вы установили другое значение для переменной numbers во время работы цикла, Java уже кэшировала исходное значение numbers и выполняет итерацию по этому значению.Если вы напечатаете значения в numbers после цикла, вы увидите, что numbers изменился, но фактический массив, по которому перебирался цикл for, не изменился.

...
public test() {

    int[] numbers = getNumbers();

    System.out.println("Array before loop starts:");
    System.out.println(Arrays.toString(numbers) + "\n");

    for (int number : numbers) {
        System.out.println("number    : " + number);
        System.out.println("numbers[0]: " + numbers[0]);

        numbers = getNumbers();
    }

    System.out.println("\nArray after loop finishes:");
    System.out.println(Arrays.toString(numbers));

}
...

Вышеприведенный вывод:

Array before loop starts:
[1, 1, 1, 1, 1]

number    : 1
numbers[0]: 1
number    : 1
numbers[0]: 2
number    : 1
numbers[0]: 3
number    : 1
numbers[0]: 4
number    : 1
numbers[0]: 5

Array after loop finishes:
[6, 6, 6, 6, 6]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...