Given the following code snippet:
int[] arr = {1, 2, 3};
for (int i : arr)
System.out.println(i);
I have the following questions:
Google Guava Libraries collection provides such function:
Iterator<String> it = Iterators.forArray(array);
One should prefere Guava over the Apache Collection (which seems to be abandoned).
If you want an Iterator
over an array, you could use one of the direct implementations out there instead of wrapping the array in a List
. For example:
Apache Commons Collections ArrayIterator
Or, this one, if you'd like to use generics:
com.Ostermiller.util.ArrayIterator
Note that if you want to have an Iterator
over primitive types, you can't, because a primitive type can't be a generic parameter. E.g., if you want an Iterator<int>
, you have to use an Iterator<Integer>
instead, which will result in a lot of autoboxing and -unboxing if that's backed by an int[]
.
I'm a bit late to the game, but I noticed some key points that were left out, particularly regarding Java 8 and the efficiency of Arrays.asList
.
As Ciro Santilli 六四事件 法轮功 包卓轩 pointed out, there's a handy utility for examining bytecode that ships with the JDK: javap
. Using that, we can determine that the following two code snippets produce identical bytecode as of 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);
}
}
While this doesn't work for primitives, it should be noted that converting an array to a List with Arrays.asList
does not impact performance in any significant way. The impact on both memory and performance is nearly immeasurable.
Arrays.asList
does not use a normal List implementation that is readily accessible as a class. It uses java.util.Arrays.ArrayList
, which is not the same as java.util.ArrayList
. It is a very thin wrapper around an array and cannot be resized. Looking at the source code for java.util.Arrays.ArrayList
, we can see that it's designed to be functionally equivalent to an array. There is almost no overhead. Note that I have omitted all but the most relevant code and added my own comments.
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;
}
}
}
The iterator is at java.util.AbstractList.Itr
. As far as iterators go, it's very simple; it just calls get()
until size()
is reached, much like a manual for loop would do. It's the simplest and usually most efficient implementation of an Iterator
for an array.
Again, Arrays.asList
does not create a java.util.ArrayList
. It's much more lightweight and suitable for obtaining an iterator with negligible overhead.
As others have noted, Arrays.asList
can't be used on primitive arrays. Java 8 introduces several new technologies for dealing with collections of data, several of which could be used to extract simple and relatively efficient iterators from arrays. Note that if you use generics, you're always going to have the boxing-unboxing problem: you'll need to convert from int to Integer and then back to int. While boxing/unboxing is usually negligible, it does have an O(1) performance impact in this case and could lead to problems with very large arrays or on computers with very limited resources (i.e., SoC).
My personal favorite for any sort of array casting/boxing operation in Java 8 is the new stream API. For example:
int[] arr = {1, 2, 3};
Iterator<Integer> iterator = Arrays.stream(arr).mapToObj(Integer::valueOf).iterator();
The streams API also offers constructs for avoiding the boxing issue in the first place, but this requires abandoning iterators in favor of streams. There are dedicated stream types for int, long, and double (IntStream, LongStream, and DoubleStream, respectively).
int[] arr = {1, 2, 3};
IntStream stream = Arrays.stream(arr);
stream.forEach(System.out::println);
Interestingly, Java 8 also adds java.util.PrimitiveIterator
. This provides the best of both worlds: compatibility with Iterator<T>
via boxing along with methods to avoid boxing. PrimitiveIterator has three built-in interfaces that extend it: OfInt, OfLong, and OfDouble. All three will box if next()
is called but can also return primitives via methods such as nextInt()
. Newer code designed for Java 8 should avoid using next()
unless boxing is absolutely necessary.
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);
}
If you're not yet on Java 8, sadly your simplest option is a lot less concise and is almost certainly going to involve boxing:
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++];
}
};
Or if you want to create something more reusable:
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++];
}
}
You could get around the boxing issue here by adding your own methods for obtaining primitives, but it would only work with your own internal code.
No, it is not. However, that doesn't mean wrapping it in a list is going to give you worse performance, provided you use something lightweight such as Arrays.asList
.
For (2), Guava provides exactly what you want as Int.asList(). There is an equivalent for each primitive type in the associated class, e.g., Booleans
for boolean
, etc.
int[] arr={1,2,3};
for(Integer i : Ints.asList(arr)) {
System.out.println(i);
}
Like many other array features, the JSL mentions arrays explicitly and gives them magical properties. JLS 7 14.14.2:
EnhancedForStatement:
for ( FormalParameter : Expression ) Statement
[...]
If the type of Expression is a subtype of
Iterable
, then the translation is as follows[...]
Otherwise, the Expression necessarily has an array type,
T[]
. [[ MAGIC! ]]Let
L1 ... Lm
be the (possibly empty) sequence of labels immediately preceding the enhanced for statement.The enhanced for statement is equivalent to a basic for statement of the form:
T[] #a = Expression;
L1: L2: ... Lm:
for (int #i = 0; #i < #a.length; #i++) {
VariableModifiersopt TargetType Identifier = #a[#i];
Statement
}
#a
and#i
are automatically generated identifiers that are distinct from any other identifiers (automatically generated or otherwise) that are in scope at the point where the enhanced for statement occurs.
Let's javap
it up:
public class ArrayForLoop {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
for (int i : arr)
System.out.println(i);
}
}
then:
javac ArrayForLoop.java
javap -v ArrayForLoop
main
method with a bit of editing to make it easier to read:
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
Breakdown:
0
to 14
: create the array15
to 22
: prepare for the for loop. At 22, store integer 0
from stack into local position 4
. THAT is the loop variable.24
to 47
: the loop. The loop variable is retrieved at 31
, and incremented at 44
. When it equals the array length which is stored in local variable 3 on the check at 27
, the loop ends.Conclusion: it is the same as doing an explicit for loop with an index variable, no itereators involved.
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();
}
}