3

Method 1

ArrayList<String> list = new ArrayList<String>();

for (String s : list) {
    write.append(s);
    write.append('\n');
}

How is looping through an ArrayList this way possible? isn't this method only applicable for static usual arrays, for example :

String[] list2 = new String[]{"1","2","3","4"}; ?

If I want to loop over the ArrayList<String> list, why not doing this for example:

Method 2

for (int i = 0; i < list.size(); i++) {
    write.append(list.get(i));
    write.append("\n");
}

I'm just not understanding how is it possible to use Method 1. Any explanation is welcomed.

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
ARMAGEDDON
  • 939
  • 3
  • 11
  • 23
  • 1
    search for "enhanced for loop" and "iterables" and read about them! – Juvanis Jul 21 '13 at 05:22
  • No this is not a duplication. I want to know the reason, there's no explanation for why and how is that possible. – ARMAGEDDON Jul 21 '13 at 05:23
  • I realized that - here's [a better resource](http://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html). – Makoto Jul 21 '13 at 05:23
  • @WilliamFox I didn't vote for closing, just typed my suggestion. – Juvanis Jul 21 '13 at 05:24
  • same.. no explanation on how could the iteration work fine without problem using method 1 with the `ArrayList` while it's not an `Array`. When I say array I mean something like `String[] bla = new String[]` – ARMAGEDDON Jul 21 '13 at 05:26

5 Answers5

3

ArrayList implements Iterable which makes it provide an Iterator object, which is then user by for loop.

Any class implementing Iterable would be usable in a for loop, just like an array.

The simplified for loop is made to look same for arrays and collections, but under the hood, it uses index for arrays and iterator for collections.

S.D.
  • 29,290
  • 3
  • 79
  • 130
3

These are called for-each loops, and can be applied to both arrays and collections. Format:

for ([type] [var-name] : [array or iterable value]) {
    ...
}

To use a for-each loop, you only need to satisfy one of two conditions:

  1. The iteration value is an array, or
  2. the iteration value is an Iterable.

The biggest reason we do this is because List isn't the only Iterable that we can use in the loop, but it is the only one that can be indexed. This allows you to do generic iteration instead of locking yourself into a fixed, indexed list format.

For example, HashSet is not ordered, but I can still iterate over it in a for-each loop:

HashSet<String> strs = new HashSet<String>();
for (String str : strs) { ... }

I can also have a method take an Iterable instead of a List:

public static void printStrings(Iterable<String> strs) {
    for (String str : strs) {
        System.out.println(str);
    }
}

There is no way for me to iterate over it using an index without first copying it into something else, like an array or a List.

Another important reason to use the for-each loop is that it compiles to using the Iterator off the list (via Iterable.iterator()), which allows for fail-fast iteration. This results in things like ConcurrentModificationExceptions to tell us that the collection was modified while we were iterating over it. For example, this will always throw a ConcurrentModificationException:

List<String> strs = ... ;
for (String str : strs) {
    strs.remove(str);
}

But this won't:

List<String> strs = ... ;
for (int i = 0; i < strs.length(); i++) {
    strs.remove(str);
}

This is good for multi-threaded applications because you're supposed to lock the list before you access it since most List implementations aren't designed for thread-safety, and iteration is never thread-safe without external synchronization, both with indexing and with Iterator. If one thread is iterating over it while another changes it, this can (not guaranteed, but can) throw a ConcurrentModificationException, which tells you that the list isn't properly locked. The indexed for could never give you this kind of information. Instead, it will just exhibit strange behavior like skipping elements.

Brian
  • 17,079
  • 6
  • 43
  • 66
2

There is an interface called Iterable which allows fast iteration. If something implements Iterable, fast iteration is allowed. The Iterable interface has one method, which returns an Iterator which can be used to iterate the elements.

tbodt
  • 16,609
  • 6
  • 58
  • 83
1

This is the for-each syntax: http://docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html

The compiler will convert your code in method 1 to something like:

for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
  String s = iterator.next();
  write.append(s);
  write.append('\n');
}
SimonC
  • 6,590
  • 1
  • 23
  • 40
  • That only applies for collections that implement `Iterable`. Arrays don't implement `Iterable`, so this doesn't apply. – Makoto Jul 21 '13 at 06:09
  • And it's wrong because there is no `getIterator` method. – Marko Topolnik Jul 21 '13 at 06:35
  • @Makoto, the question was "I'm just not understanding how is it possible to use *Method 1.". Which is what I answered. – SimonC Jul 21 '13 at 11:51
  • @MarkoTopolnik, it would have been a more useful comment had you stated that `getIterator` should be `iterator`. I don't think that typo makes the answer wrong though. – SimonC Jul 21 '13 at 11:53
  • It's still wrong because `list` is not the name of the iterator variable. BTW by "it" I mean "the code" and not "the answer". Also, I am sorry if I'm not able to hit the right way of explanation that would be the most useful to you personally. – Marko Topolnik Jul 21 '13 at 13:53
  • @MarkoTopolnik, thanks. It's more useful to point out the solution as well as stating that something is incorrect. – SimonC Jul 21 '13 at 23:21
  • It may be more useful, but it can also come off as belittling. Giving just the right amount of hint is more respectful. – Marko Topolnik Jul 22 '13 at 05:25
1

The first is called an enhanced-for, or "for each". It is considered syntactic sugar for the traditional model of for statements - allowing you to iterate over every element in a collection - be that an array or anything in the Java Collections Framework.

There are two distinct differences between these types of loops:

  • The enhanced for statement indexes the value for you, storing it in the temporary variable as part of the declaration. Thus, you don't have to do things like val[i] or value.get(i) - this happens for you automatically.
  • You don't get to specify what you increment by in the enhanced for statement; you either iterate over all elements in the collection, or you terminate early via a break statement.

The reason that this can be iterated over using a standard array is defined in the JLS, specifically §14.14.2 - since an array doesn't implement Iterable, it converts it to a standard-behaving for statement.

  • ...Otherwise, the Expression necessarily has an array type, T[].

    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++) {
    VariableModifiers(opt) 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.

Makoto
  • 104,088
  • 27
  • 192
  • 230