给出以下代码片段:
int[] arr = {1, 2, 3};
for (int i : arr)
System.out.println(i);
我有以下问题:
- 上面的for-each循环如何工作?
- 如何在Java中获得数组的迭代器?
- 数组是否转换为列表以获取迭代器?
给出以下代码片段:
int[] arr = {1, 2, 3};
for (int i : arr)
System.out.println(i);
我有以下问题:
Answers:
如果您要Iterator
遍历数组,则可以使用其中的直接实现之一,而不用将数组包装在中List
。例如:
Apache Commons集合 ArrayIterator
或者,如果您想使用泛型,请执行以下操作:
com.Ostermiller.util.ArrayIterator
请注意,如果您想拥有一个Iterator
超过基本类型的对象,则不能这样做,因为基本类型不能是通用参数。例如,如果要使用Iterator<int>
,则必须Iterator<Integer>
改用,如果后面有,则会导致大量自动装箱和-unboxing int[]
。
ArrayIt
;假设所有第三方图书馆。
不,没有转换。JVM只是在后台使用索引来遍历数组。
引用来自有效Java第二版,条款46:
请注意,即使对于数组,使用for-each循环也不会降低性能。实际上,在某些情况下,它可能只比普通的for循环提供一点性能优势,因为它只计算一次数组索引的限制。
因此,您无法获得Iterator
用于数组的(除非将其转换为第一个数组List
)。
for
。@Emil,你不能。
Arrays.asList(arr).iterator();
或编写自己的实现ListIterator接口。
Arrays.asList()
不能使用int[]
,Object[]
和子类。
List<int[]>
,因此返回一个,而Iterator<int[]>
不是一个List<Integer>
!
Arrays.asList()
使用实际的数组并将其包装在一个使它适合列表接口的类中。因此,除了“不支持原语”问题(对于在非原始情况下遇到相同问题的人)而言,这实际上是一个很好的解决方案。
Google Guava Librarie的集合提供了以下功能:
Iterator<String> it = Iterators.forArray(array);
人们应该更喜欢Guava而不是Apache Collection(后者似乎已被放弃)。
int[]
)时,这将不再起作用:(因为我们无法指定Iterator<int>
或Iterator<Integer>
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()
来代替。
您不能直接获得数组的迭代器。
但是您可以使用数组支持的列表,并在此列表上获得一个迭代器。为此,您的数组必须是Integer数组(而不是int数组):
Integer[] arr={1,2,3};
List<Integer> arrAsList = Arrays.asList(arr);
Iterator<Integer> iter = arrAsList.iterator();
注意:这只是理论上的。您可以得到这样的迭代器,但我不鼓励您这样做。与使用“扩展语法”的数组直接迭代相比,性能不佳。
注意2:使用此方法的列表构造不支持所有方法(因为列表由具有固定大小的数组支持)。例如,迭代器的“删除”方法将导致异常。
Arrays.asList
通过将数组中的值装箱来隐式地解决此问题。因此,结果真的是一个List<Integer>
。
像许多其他数组功能一样,JSL明确提到数组并赋予它们神奇的属性。JLS 7 14.14.2:
EnhancedForStatement:
for ( FormalParameter : Expression ) Statement
[...]
如果Expression的类型是的子类型
Iterable
,则翻译如下[...]
否则,表达式必须具有数组类型
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
to 14
:创建数组15
to 22
:为for循环做准备。在22,将整数0
从堆栈存储到本地位置4
。那就是循环变量。24
到47
:循环。在处检索循环变量31
,并在处递增44
。当它等于数组长度,该长度存储在check处的局部变量3中时27
,循环结束。结论:这与使用索引变量进行显式for循环相同,不涉及迭代器。
我玩游戏有些迟了,但是我注意到一些关键点被遗漏了,特别是在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);
}
}
尽管这不适用于原语,但应注意,将数组转换为具有的ListArrays.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>
via装箱以及避免装箱的方法兼容。PrimitiveIterator具有三个扩展它的内置接口:OfInt,OfLong和OfDouble。如果next()
调用,这三个函数都将置为box,但也可以通过诸如之类的方法返回原语nextInt()
。next()
除非绝对需要装箱,否则应避免使用为Java 8设计的较新代码。
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
。
mapToObj(Integer::new)
为mapToObj(Integer::valueOf)
对于(2),Guava提供的正是Int.asList()。关联类中的每个原始类型都有一个等效项,例如Booleans
forboolean
等。
int[] arr={1,2,3};
for(Integer i : Ints.asList(arr)) {
System.out.println(i);
}
我是一个新来的学生,但是我相信int []的原始示例是遍历图元数组,而不是通过使用Iterator对象。它只有具有不同内容的相同(相似)语法,
for (primitive_type : array) { }
for (object_type : iterableObject) { }
Arrays.asList()大概只是将List方法应用于给定的对象数组-但是对于任何其他类型的对象,包括原始数组,iterator()。next()显然只会将您对原始对象的引用交给您,它作为一个元素的列表。我们可以看到源代码吗?您不喜欢例外吗?没关系。我猜(这是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;
}
};
}