次のコードスニペットがあるとします。
int[] arr = {1, 2, 3};
for (int i : arr)
System.out.println(i);
次の質問があります。
配列にIterator
が必要な場合は、List
で配列をラップする代わりに、直接実装のいずれかを使用できます。例えば:
Apache Commons Collections ArrayIterator
または、ジェネリックを使用する場合は、次のようにします:
com.Ostermiller.util.ArrayIterator
プリミティブ型を汎用パラメーターにできないため、Iterator
をプリミティブ型にしたい場合はできないことに注意してください。たとえば、Iterator<int>
が必要な場合は、代わりにIterator<Integer>
を使用する必要があります。これにより、int[]
によってサポートされている場合、多くのオートボクシングと-unboxingが行われます。
いいえ、変換はありません。 JVMは、バックグラウンドでインデックスを使用して配列を反復処理します。
Effective Java 2nd Ed。、Item 46からの引用:
For-eachループを使用しても、配列であってもパフォーマンスが低下することはありません。実際、状況によっては、配列インデックスの制限を1回しか計算しないため、通常のforループよりもわずかにパフォーマンスが向上する場合があります。
したがって、配列のIterator
を取得することはできません(もちろん、最初にList
に変換しない限り)。
Arrays.asList(arr).iterator();
または、独自のListIteratorインターフェースを実装します。
Google Guava Librarie sコレクションはそのような機能を提供します:
Iterator<String> it = Iterators.forArray(array);
Apache Collection(放棄されたようです)よりもGuavaを好むべきです。
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[]>
を返します。代わりに Guava のInts.asList()
を使用してください。
配列の反復子を直接取得することはできません。
ただし、配列を基にしたリストを使用して、このリストのイテレーターを取得できます。そのためには、配列は整数配列(int配列ではなく)でなければなりません:
Integer[] arr={1,2,3};
List<Integer> arrAsList = Arrays.asList(arr);
Iterator<Integer> iter = arrAsList.iterator();
注:それは理論にすぎません。このようなイテレータを入手できますが、そうすることはお勧めしません。 「extended for syntax」を使用した配列の直接反復と比較すると、パフォーマンスは良くありません。
注2:このメソッドを使用したリストコンストラクトは、すべてのメソッドをサポートしているわけではありません(リストは固定サイズの配列によってサポートされているため)。たとえば、イテレータの「remove」メソッドは例外になります。
他の多くの配列機能と同様に、JSLは配列を明示的に言及し、それらに魔法のプロパティを与えます。 JLS 7 14.14.2 :
EnhancedForStatement:
for ( FormalParameter : Expression ) Statement
[...]
Expressionのタイプが
Iterable
のサブタイプである場合、変換は次のようになります[...]
それ以外の場合、Expressionには必ず
T[]
という配列型が必要です。 [[ 魔法! ]]
L1 ... Lm
を、拡張forステートメントの直前の(おそらく空の)ラベルのシーケンスとします。拡張forステートメントは、次の形式の基本forステートメントと同等です。
T[] #a = Expression;
L1: L2: ... Lm:
for (int #i = 0; #i < #a.length; #i++) {
VariableModifiersopt TargetType Identifier = #a[#i];
Statement
}
#a
および#i
は、自動生成された識別子であり、拡張forステートメントが発生する時点で有効範囲内にある他の識別子(自動生成またはその他)とは異なります。
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
から14
:配列の作成15
から22
:forループの準備。 22で、整数0
をスタックからローカル位置4
に保存します。これがループ変数です。24
から47
:ループ。ループ変数は31
で取得され、44
でインクリメントされます。 27
のチェックでローカル変数3に格納されている配列の長さと等しい場合、ループは終了します。結論:インデックス変数を使用して明示的な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とArrays.asList
の効率性に関して、いくつかの重要なポイントが残されていることに気付きました。
Ciro Santilli六四事件法轮功包卓轩 が指摘したように、JDKに同梱されているバイトコードを調べるための便利なユーティリティjavap
があります。これを使用して、次の2つのコードスニペットがJava 8u74の時点で同一のバイトコードを生成すると判断できます。
int[] arr = {1, 2, 3};
for (int n : arr) {
System.out.println(n);
}
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);
}
}
これはプリミティブでは機能しませんが、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
にあります。イテレータに関する限り、それは非常に単純です。 get()
に到達するまでsize()
を呼び出すだけです。マニュアルforループの場合と同様です。これは、配列のIterator
の最も単純で通常最も効率的な実装です。
繰り返しますが、Arrays.asList
はJava.util.ArrayList
を作成しません。はるかに軽量で、オーバーヘッドが無視できるイテレータを取得するのに適しています。
他の人が指摘したように、Arrays.asList
はプリミティブ配列では使用できません。 Java 8には、データのコレクションを処理するためのいくつかの新しいテクノロジーが導入されており、そのいくつかは、配列から単純で比較的効率的なイテレーターを抽出するために使用できます。ジェネリックを使用する場合は、常にボクシングとアンボクシングの問題が発生することに注意してください。intからIntegerに変換してから、intに戻す必要があります。通常、ボクシング/アンボクシングは無視できますが、この場合は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には、それを拡張する3つの組み込みインターフェース、OfInt、OfLong、およびOfDoubleがあります。 next()
が呼び出された場合、3つすべてがボックスになりますが、nextInt()
などのメソッドを介してプリミティブを返すこともできます。 Java 8用に設計された新しいコードは、ボクシングが絶対に必要でない限り、next()
の使用を避ける必要があります。
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++];
}
}
プリミティブを取得する独自のメソッドを追加することで、ここでボクシングの問題を回避できますが、独自の内部コードでのみ機能します。
いいえそうではありません。ただし、Arrays.asList
などの軽量のものを使用する場合、リストにラップすることでパフォーマンスが低下するわけではありません。
私は最近の学生ですが、int []を使用した元の例はプリミティブ配列を反復していますが、Iteratorオブジェクトは使用していません。内容が異なる同じ(類似の)構文を持っているだけで、
for (primitive_type : array) { }
for (object_type : iterableObject) { }
Arrays.asList()は、指定されたオブジェクト配列にListメソッドを適用しますが、プリミティブ配列を含む他の種類のオブジェクトの場合、iterator()。next()は、元のオブジェクトへの参照を処理します。 1つの要素を持つリストとして。これのソースコードを見ることができますか?例外を好みませんか?気にしないで。私は推測します(それはGUESSです)それは、シングルトンコレクションに似ている(またはそうである)と思います。そのため、ここではasList()はプリミティブ配列の場合とは無関係ですが、混乱を招きます。私は自分が正しいとは知りませんが、私がそうだと言うプログラムを書きました。
したがって、この例(基本的にasList()はあなたが思っていることをしないので、実際にこのように使用するものではありません)-コードがコードとしてのマーキングよりもうまく機能することを願っていますちょっと、最後の行を見てください:
// 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());
}
}
グアバのIterators
を使用して、30日からの回答が好きです。ただし、一部のフレームワークでは、空の配列ではなくnullを取得し、Iterators.forArray(array)
はそれをうまく処理しません。そこで、このヘルパーメソッドを思い付きました。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;
}
};
}