Enhanced for 문은 배열에 대해 어떻게 작동하며 배열에 대한 반복기를 가져 오는 방법은 무엇입니까?
다음 코드 스 니펫이 제공됩니다.
int[] arr = {1, 2, 3};
for (int i : arr)
System.out.println(i);
다음과 같은 질문이 있습니다.
- 위의 for-each 루프는 어떻게 작동합니까?
- Java에서 배열에 대한 반복자를 어떻게 얻습니까?
- 반복자를 얻기 위해 배열이 목록으로 변환됩니까?
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
고장:
0
to14
: 배열 만들기15
to22
: for 루프를 준비합니다. 에서 22 정수 저장0
지역에 위치에서 스택4
. 그것은 루프 변수입니다.24
to47
: 루프. 루프 변수는에서 검색되고에서31
증가44
합니다. 체크에서 로컬 변수 3에 저장된 배열 길이와 같으면27
루프가 종료됩니다.
결론 : 인덱스 변수를 사용하여 명시적인 for 루프를 수행하는 것과 동일하며 반복자가 포함되지 않습니다.
(2)의 경우 Guava는 Int.asList () 로 원하는 것을 정확하게 제공합니다 . 연관된 클래스의 각 기본 유형에 대해 등가가 있습니다 (예 : Booleans
for 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;
}
};
}
'Program Tip' 카테고리의 다른 글
둘 이상의 iPhone 응용 프로그램간에 데이터 공유 (0) | 2020.10.15 |
---|---|
JVM을 실행해야 할 때 Java 플랫폼에 어떻게 독립적입니까? (0) | 2020.10.15 |
토글 버튼 Android의 켜기 / 끄기 텍스트 변경 (0) | 2020.10.15 |
grunt를 설치하는 방법 및 스크립트를 작성하는 방법 (0) | 2020.10.15 |
이 부울 "(number & 1) == 0"은 무엇을 의미합니까? (0) | 2020.10.15 |