Home Advanced loops
Advanced loops
In CSE 142, we learned two basic looping constructs: for
loops and
while
loops. In CSE 143, we learn about more advanced looping constructs such as
Iterators
(which must be used in combination with some other looping technique)
and foreach
loops.
Iterators
Use iterators when you want to use a foreach
loop, but also want to
simultaneously modify the collection you are iterating over. Prefer using
foreach
loops whenever possible.
Iterators should primarily be used for the special case when we need to iterate over a collection while modifying it by either adding or removing elements. While you might think that a foreach loop would be a good choice here, Java will actually throw a ConcurrentModificationException
if you try.
So, we are forced to use iterators out of necessity. For example:
public static void removeBadCandies(List<String> candies) { Iterator<String> iter = candies.iterator(); while (iter.hasNext()) { String candy = iter.next(); if (candy.startsWith("rotten")) { iter.remove(); } } }
Sometimes, you will find that it's nicer to use a regular for loop in this particular case. If so, feel free to do so.
Also note that foreach loops are actually internally using iterators, except in a nicer syntax – you don't lose anything by switching to foreach loops if it's possible to do so.
Foreach loops
Use a foreach
loop when you want to iterate over some sort of collection such
as an array or a list, and work on the collection one element at a time. Prefer using
foreach
loops over for
loops and manually manipulating
Iterators
whenever possible.
One way of thinking about foreach loops is that they're a more specific kind of for loops geared specifically towards iterating over collections or streams. As it turns out, this is a really common sort of thing to do – there are even some programming languages which have eliminated for loops entirely in favor of foreach loops (e.g. Python)
A good example of when to use a foreach loop is when you want to iterate over a list:
public static int sum(List<Integer> numbers) { int total = 0; for (int number : numbers) { total += number; } return total; }
Contrast this to using regular for loops:
public static int sum(List<Integer> numbers) { int total = 0; for (int i = 0; i < numbers.size(); i++) { int number = numbers.get(i); total += number; } return total; }
In comparison to the first version, this is much more verbose and clunky – foreach loops are just more elegant in general.
The primary exception to this rule is when you need both the loop counter and want to iterate over a collection. In this case, it'd probably be better to just default to using a regular for loop since we need the counter anyways:
public static void inspect(List<Integer> numbers) { for (int i = 0; i < numbers.size(); i++) { int number = numbers.get(i); System.out.println("arr[" + i + "] => " + number); } }
There are some programming languages which allow you to neatly handle this case and still use a foreach loop, but alas Java is not one of them so we must compromise.
The other exception is if you want to modify a collection while iterating over it, which is covered in the following section.
Recursion
Use recursion when none of the above appear to be a good fit when looping, and when simple iteration will not suffice. For example, recursion is often a good fit when dealing with branching data structures such as trees.
Recursion is a complex topic which we don't have room to discuss here – it's mostly included for completeness since it is possible to solve any problem involving iteration using recursion. In general, you should not be using recursion as a replacement for any of the above constructs, unless explicitly told otherwise or unless you feel doing so would result in cleaner or more efficient code.
We will discuss recursion in more detail later in the course, and later within this style guide.
Higher-order functions
Do not use higher-order functions (streams, lambdas, map, etc).
Java includes a few more constructs which can be used for looping and iteration as of Java 8 – namely, lambdas, streams, and higher-order functions. While those constructs are definitely useful and interesting, they will not be covered in CSE 142 and 143 and will constitute advanced material.