Program Tip

Enhanced for 문은 배열에 대해 어떻게 작동하며 배열에 대한 반복기를 가져 오는 방법은 무엇입니까?

programtip 2020. 10. 15. 21:30
반응형

Enhanced for 문은 배열에 대해 어떻게 작동하며 배열에 대한 반복기를 가져 오는 방법은 무엇입니까?


다음 코드 스 니펫이 제공됩니다.

int[] arr = {1, 2, 3};
for (int i : arr)
    System.out.println(i);

다음과 같은 질문이 있습니다.

  1. 위의 for-each 루프는 어떻게 작동합니까?
  2. Java에서 배열에 대한 반복자를 어떻게 얻습니까?
  3. 반복자를 얻기 위해 배열이 목록으로 변환됩니까?

Iterator배열을 통해 배열 을 원하는 경우 배열을 .NET 파일로 래핑하는 대신 직접 구현 중 하나를 사용할 수 있습니다 List. 예를 들면 :

Apache Commons 컬렉션 ArrayIterator

또는 제네릭을 사용하려면 다음을 수행하십시오.

com.Ostermiller.util.ArrayIterator

Iterator기본 유형이 일반 매개 변수가 될 수 없기 때문에 기본 유형을 초과 하려는 경우 그렇게 할 수 없습니다. 예를 들어를 원하는 경우 대신 Iterator<int>을 사용해야 Iterator<Integer>합니다 int[]. 이 경우 .


아니요, 변환이 없습니다. JVM은 백그라운드에서 인덱스를 사용하여 배열을 반복합니다.

Effective Java 2nd Ed., 항목 46의 인용문 :

배열의 경우에도 for-each 루프를 사용하면 성능이 저하되지 않습니다. 실제로 배열 인덱스의 한계를 한 번만 계산하기 때문에 일부 상황에서 일반 for 루프에 비해 약간의 성능 이점을 제공 할 수 있습니다.

따라서 Iterator배열에 대한를 얻을 수 없습니다 (물론 List첫 번째 로 변환하지 않는 한 ).


Arrays.asList (arr) .iterator ();

또는 직접 ListIterator 인터페이스를 구현하여 작성하십시오.


Google Guava Librarie 컬렉션은 다음과 같은 기능을 제공합니다.

Iterator<String> it = Iterators.forArray(array);

아파치 컬렉션보다 구아바를 선호해야합니다.


Java 8에서

Arrays.stream(arr).iterator();

public class ArrayIterator<T> implements Iterator<T> {
  private T array[];
  private int pos = 0;

  public ArrayIterator(T anArray[]) {
    array = anArray;
  }

  public boolean hasNext() {
    return pos < array.length;
  }

  public T next() throws NoSuchElementException {
    if (hasNext())
      return array[pos++];
    else
      throw new NoSuchElementException();
  }

  public void remove() {
    throw new UnsupportedOperationException();
  }
}

엄밀히 말하면, Iterator.next () 는 Object 만 반환 할 수 있기 때문에 기본 배열의 반복자를 가져올 수 없습니다 . 그러나 오토 박싱의 마법을 통해 Arrays.asList () 메서드를 사용하여 반복자를 얻을 수 있습니다 .

Iterator<Integer> it = Arrays.asList(arr).iterator();

위의 대답은 잘못 Arrays.asList()되었습니다. 기본 배열에서 사용할 수 없으며 List<int[]>. 사용 구아바Ints.asList()대신.


배열에 대한 반복기를 직접 가져올 수는 없습니다.

그러나 배열로 뒷받침되는 List를 사용하고이 목록에서 ierator를 얻을 수 있습니다. 이를 위해 배열은 정수 배열이어야합니다 (int 배열 대신).

Integer[] arr={1,2,3};
List<Integer> arrAsList = Arrays.asList(arr);
Iterator<Integer> iter = arrAsList.iterator();

참고 : 이것은 이론 일뿐입니다. 이와 같은 반복자를 얻을 수 있지만 그렇게하지 않는 것이 좋습니다. 성능은 "신택스 확장"을 사용하는 어레이에서 직접 반복하는 것에 비해 좋지 않습니다.

참고 2 :이 메서드를 사용하는 목록 구성은 모든 메서드를 지원하지 않습니다 (목록이 고정 된 크기를 가진 배열에 의해 뒷받침되기 때문에). 예를 들어 반복자의 "remove"메소드는 예외를 발생시킵니다.


위의 for-each 루프는 어떻게 작동합니까?

다른 많은 배열 기능과 마찬가지로 JSL은 배열을 명시 적으로 언급하고 마법의 속성을 부여합니다. JLS 7 14.14.2 :

EnhancedForStatement:

    for ( FormalParameter : Expression ) Statement

[...]

Expression 유형이의 하위 유형 인 Iterable경우 번역은 다음과 같습니다.

[...]

그렇지 않으면 Expression에는 반드시 배열 유형 인 T[]. [[마법! ]]

하자 L1 ... Lm즉시 문에 대한 향상된 이전 라벨 (하늘의) 순서합니다.

향상된 for 문은 다음 형식의 기본 for 문과 동일합니다.

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

#a#i자동으로 문이 발생위한 향상된 점 범위에있는 임의의 다른 식별자 (자동으로 생성하거나 그렇지) 구별되는 식별자를 생성한다.

반복자를 얻기 위해 배열이 목록으로 변환됩니까?

해보자 javap:

public class ArrayForLoop {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        for (int i : arr)
            System.out.println(i);
    }
}

그때:

javac ArrayForLoop.java
javap -v ArrayForLoop

main 좀 더 쉽게 읽을 수 있도록 약간의 편집이 필요합니다.

 0: iconst_3
 1: newarray       int
 3: dup
 4: iconst_0
 5: iconst_1
 6: iastore
 7: dup
 8: iconst_1
 9: iconst_2
10: iastore
11: dup
12: iconst_2
13: iconst_3
14: iastore

15: astore_1
16: aload_1
17: astore_2
18: aload_2
19: arraylength
20: istore_3
21: iconst_0
22: istore        4

24: iload         4
26: iload_3
27: if_icmpge     50
30: aload_2
31: iload         4
33: iaload
34: istore        5
36: getstatic     #2    // Field java/lang/System.out:Ljava/io/PrintStream;
39: iload         5
41: invokevirtual #3    // Method java/io/PrintStream.println:(I)V
44: iinc          4, 1
47: goto          24
50: return

고장:

  • 0to 14: 배열 만들기
  • 15to 22: for 루프를 준비합니다. 에서 22 정수 저장 0지역에 위치에서 스택 4. 그것은 루프 변수입니다.
  • 24to 47: 루프. 루프 변수는에서 검색되고에서 31증가 44합니다. 체크에서 로컬 변수 3에 저장된 배열 길이와 같으면 27루프가 종료됩니다.

결론 : 인덱스 변수를 사용하여 명시적인 for 루프를 수행하는 것과 동일하며 반복자가 포함되지 않습니다.


(2)의 경우 Guava는 Int.asList () 로 원하는 것을 정확하게 제공합니다 . 연관된 클래스의 각 기본 유형에 대해 등가가 있습니다 (예 : Booleansfor boolean등).

    int[] arr={1,2,3};
    for(Integer i : Ints.asList(arr)) {
      System.out.println(i);
    }

게임에 조금 늦었지만 특히 Java 8과 .NET의 효율성과 관련하여 빠진 몇 가지 핵심 사항을 발견했습니다 Arrays.asList.

1. for-each 루프는 어떻게 작동합니까?

치로 틸리六四事件法轮功包卓轩뾰족한 밖으로, 바이트 코드를 검사하는 것을 JDK와 함께 제공되는 편리한 유틸리티있다 : javap. 이를 사용하여 다음 두 코드 조각이 Java 8u74와 동일한 바이트 코드를 생성하는지 확인할 수 있습니다.

For-each 루프 :

int[] arr = {1, 2, 3};
for (int n : arr) {
    System.out.println(n);
}

For 루프 :

int[] arr = {1, 2, 3};

{  // These extra braces are to limit scope; they do not affect the bytecode
    int[] iter = arr;
    int length = iter.length;
    for (int i = 0; i < length; i++) {
        int n = iter[i];
        System.out.println(n);
    }
}

2. Java에서 배열에 대한 반복자를 어떻게 얻습니까?

이것은 프리미티브에서 작동하지 않지만 배열을 List로 변환 Arrays.asList해도 성능에 큰 영향 미치지 않습니다. 메모리와 성능에 미치는 영향은 거의 측정 할 수 없습니다.

Arrays.asList클래스로 쉽게 액세스 할 수있는 일반 List 구현을 사용하지 않습니다. 를 사용 java.util.Arrays.ArrayList하며 java.util.ArrayList. 배열 주위의 매우 얇은 래퍼이며 크기를 조정할 수 없습니다. 의 소스 코드를 보면 java.util.Arrays.ArrayList배열과 기능적으로 동일하도록 설계되었음을 알 수 있습니다. 오버 헤드가 거의 없습니다. 가장 관련성이 높은 코드를 제외하고 모두 생략하고 내 의견을 추가했습니다.

public class Arrays {
    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }

    private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable {
        private final E[] a;

        ArrayList(E[] array) {
            a = Objects.requireNonNull(array);
        }

        @Override
        public int size() {
            return a.length;
        }

        @Override
        public E get(int index) {
            return a[index];
        }

        @Override
        public E set(int index, E element) {
            E oldValue = a[index];
            a[index] = element;
            return oldValue;
        }
    }
}

이터레이터는에 java.util.AbstractList.Itr있습니다. 반복자가가는 한 매우 간단합니다. 수동 for 루프가하는 것처럼에 도달 get()할 때까지 호출합니다 size(). Iterator배열에 대한 가장 간단하고 일반적으로 가장 효율적인 구현입니다 .

다시 말하지만, Arrays.asList을 만들지 않습니다 java.util.ArrayList. 훨씬 더 가볍고 오버 헤드가 무시할 수있는 반복자를 얻는 데 적합합니다.

원시 배열

다른 사람들이 언급했듯이 Arrays.asList기본 배열에는 사용할 수 없습니다. Java 8은 데이터 모음을 처리하기위한 몇 가지 새로운 기술을 도입했으며, 그중 몇 가지는 배열에서 간단하고 상대적으로 효율적인 반복기를 추출하는 데 사용할 수 있습니다. 제네릭을 사용하는 경우 항상 boxing-unboxing 문제가 발생합니다. int에서 Integer로 변환 한 다음 다시 int로 변환해야합니다. boxing / unboxing은 일반적으로 무시할 수 있지만이 경우 O (1) 성능에 영향을 미치며 매우 큰 어레이 또는 리소스가 매우 제한된 컴퓨터 (예 : SoC ) 에서 문제를 일으킬 수 있습니다 .

Java 8에서 모든 종류의 배열 캐스팅 / 박싱 작업에서 개인적으로 가장 좋아하는 것은 새로운 스트림 API입니다. 예를 들면 :

int[] arr = {1, 2, 3};
Iterator<Integer> iterator = Arrays.stream(arr).mapToObj(Integer::valueOf).iterator();

스트림 API는 또한 처음에 권투 문제를 방지하기위한 구성을 제공하지만,이를 위해서는 스트림을 위해 반복기를 포기해야합니다. int, long 및 double에 대한 전용 스트림 유형이 있습니다 (각각 IntStream, LongStream 및 DoubleStream).

int[] arr = {1, 2, 3};
IntStream stream = Arrays.stream(arr);
stream.forEach(System.out::println);

흥미롭게도 Java 8은 java.util.PrimitiveIterator. 이것은 Iterator<T>복싱을 피하는 방법과 함께 복싱을 통한 호환성이라는 두 가지 장점을 모두 제공합니다 . PrimitiveIterator에는이를 확장하는 세 가지 내장 인터페이스 (OfInt, OfLong 및 OfDouble)가 있습니다. 세 가지 모두 next()가 호출 되면 상자에 넣을 수 있지만 같은 메서드를 통해 기본 형식을 반환 할 수도 있습니다 nextInt(). Java 8 용으로 설계된 최신 코드 next()는 boxing이 절대적으로 필요한 경우가 아니면 사용 하지 않아야 합니다.

int[] arr = {1, 2, 3};
PrimitiveIterator.OfInt iterator = Arrays.stream(arr);

// You can use it as an Iterator<Integer> without casting:
Iterator<Integer> example = iterator;

// You can obtain primitives while iterating without ever boxing/unboxing:
while (iterator.hasNext()) {
    // Would result in boxing + unboxing:
    //int n = iterator.next();

    // No boxing/unboxing:
    int n = iterator.nextInt();

    System.out.println(n);
}

아직 Java 8을 사용하고 있지 않다면 가장 간단한 옵션은 훨씬 덜 간결하며 거의 확실히 복싱을 포함 할 것입니다.

final int[] arr = {1, 2, 3};
Iterator<Integer> iterator = new Iterator<Integer>() {
    int i = 0;

    @Override
    public boolean hasNext() {
        return i < arr.length;
    }

    @Override
    public Integer next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }

        return arr[i++];
    }
};

또는 더 재사용 가능한 것을 만들고 싶다면 :

public final class IntIterator implements Iterator<Integer> {
    private final int[] arr;
    private int i = 0;

    public IntIterator(int[] arr) {
        this.arr = arr;
    }

    @Override
    public boolean hasNext() {
        return i < arr.length;
    }

    @Override
    public Integer next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }

        return arr[i++];
    }
}

기본 요소를 얻기위한 자체 메서드를 추가하여 여기에서 권투 문제를 해결할 수 있지만 자체 내부 코드에서만 작동합니다.

3. 반복자를 얻기 위해 배열이 목록으로 변환됩니까?

전혀 그렇지 않다. 그러나 그렇다고해서 .NET과 같은 가벼운 것을 사용하는 경우 목록으로 래핑해도 성능이 저하되는 것은 아닙니다 Arrays.asList.


저는 최근 학생이지만 int []를 사용한 원래 예제가 기본 배열을 반복하지만 Iterator 객체를 사용하지 않는다고 믿습니다. 내용이 다른 동일한 (유사한) 구문을 가지고있을뿐입니다.

for (primitive_type : array) { }

for (object_type : iterableObject) { }

Arrays.asList() APPARENTLY just applies List methods to an object array that it's given - but for any other kind of object, including a primitive array, iterator().next() APPARENTLY just hands you the reference to the original object, treating it as a list with one element. Can we see source code for this? Wouldn't you prefer an exception? Never mind. I guess (that's GUESS) that it's like (or it IS) a singleton Collection. So here asList() is irrelevant to the case with a primitives array, but confusing. I don't KNOW I'm right, but I wrote a program that says that I am.

Thus this example (where basically asList() doesn't do what you thought it would, and therefore is not something that you'd actually use this way) - I hope the code works better than my marking-as-code, and, hey, look at that last line:

// Java(TM) SE Runtime Environment (build 1.6.0_19-b04)

import java.util.*;

public class Page0434Ex00Ver07 {
public static void main(String[] args) {
    int[] ii = new int[4];
    ii[0] = 2;
    ii[1] = 3;
    ii[2] = 5;
    ii[3] = 7;

    Arrays.asList(ii);

    Iterator ai = Arrays.asList(ii).iterator();

    int[] i2 = (int[]) ai.next();

    for (int i : i2) {
        System.out.println(i);
    }

    System.out.println(Arrays.asList(12345678).iterator().next());
}
}

I like the answer from 30thh using Iterators from Guava. However, from some frameworks I get null instead of an empty array, and Iterators.forArray(array) does not handle that well. So I came up with this helper method, which you can call with Iterator<String> it = emptyIfNull(array);

public static <F> UnmodifiableIterator<F> emptyIfNull(F[] array) {
    if (array != null) {
        return Iterators.forArray(array);
    }
    return new UnmodifiableIterator<F>() {
        public boolean hasNext() {
            return false;
        }

        public F next() {
            return null;
        }
    };
}

참고URL : https://stackoverflow.com/questions/3912765/how-does-the-enhanced-for-statement-work-for-arrays-and-how-to-get-an-iterator

반응형