给出以下代码片段:
int[] arr = {1, 2, 3};
for (int i : arr)
System.out.println(i);
我有以下问题:
答案 0 :(得分:55)
如果你想要一个Iterator
数组,你可以使用其中一个直接实现,而不是将数组包装在List
中。例如:
Apache Commons Collections ArrayIterator
或者,这个,如果你想使用泛型:
com.Ostermiller.util.ArrayIterator
请注意,如果您希望Iterator
超过基元类型,则不能,因为基本类型不能是通用参数。例如,如果你想要一个Iterator<int>
,则必须使用Iterator<Integer>
代替int[]
,这将导致大量自动装箱和-unboxing,如果它由{{1}}支持。
答案 1 :(得分:50)
不,没有转换。 JVM只是在后台使用索引迭代数组。
引自Effective Java 2nd Ed。,第46项:
请注意,使用时不会有性能损失 for-each循环,即使对于数组也是如此。实际上,它可能会提供轻微的性能优势 在某些情况下,通过普通的for循环,因为它计算的限制 数组索引只有一次。
因此,您无法获得数组的Iterator
(当然,除非先将其转换为List
)。
答案 2 :(得分:33)
Arrays.asList(ARR).iterator();
或编写自己的,实现ListIterator接口..
答案 3 :(得分:31)
Google Guava Librarie的集合提供了这样的功能:
Iterator<String> it = Iterators.forArray(array);
人们应该优先选择Guava而不是Apache Collection(似乎已经放弃了)。
答案 4 :(得分:14)
在Java 8中:
Arrays.stream(arr).iterator();
答案 5 :(得分:10)
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();
}
}
答案 6 :(得分:9)
严格来说,你不能得到原始数组的迭代器,因为Iterator.next()只能返回一个Object。但是通过自动装箱的魔力,您可以使用Arrays.asList()方法获取迭代器。
Iterator<Integer> it = Arrays.asList(arr).iterator();
上面的答案是错误的,你不能在原始数组上使用Arrays.asList()
,它会返回List<int[]>
。请改用Guava的Ints.asList()
。
答案 7 :(得分:5)
您无法直接获取数组的迭代器。
但您可以使用由数组支持的List,并在此列表中获取一个ierator。为此,您的数组必须是 Integer 数组(而不是int数组):
Integer[] arr={1,2,3};
List<Integer> arrAsList = Arrays.asList(arr);
Iterator<Integer> iter = arrAsList.iterator();
注意:这只是理论。你可以得到这样的迭代器,但我不鼓励你这样做。与使用“extended for syntax”的数组上的直接迭代相比,性能不佳。
注意2:使用此方法的列表构造不支持所有方法(因为列表由具有固定大小的数组支持)。例如,迭代器的“remove”方法将导致异常。
答案 8 :(得分:4)
与许多其他数组功能一样,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循环相同,不涉及任何迭代器。
答案 9 :(得分:3)
对于(2),Guava提供了你想要的Int.asList()。关联类中的每种基本类型都有一个等价物,例如Booleans
等boolean
等。
int[] arr={1,2,3};
for(Integer i : Ints.asList(arr)) {
System.out.println(i);
}
答案 10 :(得分:3)
我有点晚了,但我注意到一些关键点,特别是关于Java 8和Arrays.asList
的效率。
正如Ciro Santilli 六四事件 法轮功 包卓轩指出的那样,有一个方便的实用工具可用于检查JDK附带的字节码:javap
。使用它,我们可以确定以下两个代码片段产生与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()
,就像循环手册一样。它是数组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有三个扩展它的内置接口:OfInt,OfLong和OfDouble。如果调用next()
,则所有三个都将为框,但也可以通过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
之类的轻量级内容,这并不意味着将其包装在列表中会给您带来更差的性能。
答案 11 :(得分:2)
我是最近的学生,但我相信int []的原始示例正在遍历基元数组,但不是通过使用Iterator对象。它只有相同(相似)的语法,内容不同,
for (primitive_type : array) { }
for (object_type : iterableObject) { }
Arrays.asList()APPARENTLY只将List方法应用于它给定的对象数组 - 但对于任何其他类型的对象,包括基本数组,iterator()。next()APPARENTLY只是将你的引用传递给原始对象对象,将其视为具有一个元素的列表。我们可以看到这个的源代码吗?你不想要一个例外吗?没关系。我猜(那是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());
}
}
答案 12 :(得分:1)
我喜欢使用来自番石榴的Iterators
来自30thh的答案。但是,从某些框架中我得到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;
}
};
}