If you have a Collection
(List
or Set
) you can use a for-each loop to visit every
element in the collection. For example, you can sum the values
of all of the elements in a Set
:
/* Set<Double> someNumbers = ... */ double sum = 0.0; for (Double d : someNumbers) { sum += d; } double average = sum / someNumbers.size();
The code fragment above has a potential problem; do you see it?
An Iterator
is an object that lets the client
traverse a collection. Iterator
is actually an
interface:
public interface Iterator<E> { boolean hasNext(); E next(); void remove(); //optional }
Every class that implements Collection
has a
method named iterator
that returns an iterator
for the elements in the collection.
Traversing a collection using an iterator uses a method similar
to the chained traversal method. Suppose you have collection of
elements that we can label e0
,
e1
, and so on.
Note that the order of the elements is determined by the container.
The first thing we need to do is to get an iterator for
the collection by calling the collection's iterator
method. This creates an iterator object that points not
at the first element e0
, but at a position
immediately before e0
.
/*
Set<Double> someNumbers = ...
*/
double sum = 0.0;
Iterator<Double> iter = someNumbers.iterator();
Inside the loop, you use the iterator to access the current
element in the traversal using the method next
.
next
returns the next element in the traversal,
and throws an exception if there are no more elements in
the traversal.
double sum = 0.0;
Iterator<Double> iter = someNumbers.iterator();
for (???)
{
sum = sum + iter.next();
}
Invoking next
has the side-effect of moving
the iterator to a position just before the next element
in the collection.
The loop body should execute only if next
will succeed (not throw an exception). The iterator method
hasNext
returns true
if
next
will succeed, and false
otherwise.
double sum = 0.0;
Iterator<Double> iter = someNumbers.iterator();
for ( ; iter.hasNext(); )
{
sum = sum + iter.next();
}
You can also use a while
loop:
double sum = 0.0;
Iterator<Double> iter = someNumbers.iterator();
while ( iter.hasNext() )
{
sum = sum + iter.next();
}
Eventually, the loop will cause the iterator to move past
the last element. At this point, hasNext
will return false
and the loop will stop.
For-each loops are really iterator loops that the compiler hides from the programmer.
For-each loops are the preferred method of traversing a collection, but it has limitations. One limitation is that you cannot remove elements from a collection using a for-each loop.
Suppose you want to remove all the numbers greater than some value from a collection (of numbers). You might try something using a for-each loop like this:
import java.util.*; public class ForEachFail { public static void main(String[] args) { // Create list of numbers from 0-9 Listnumbers = new ArrayList<Double>(); for (int i = 0; i < 10; i++) { numbers.add((double)i); } // Remove 5-9 ? final double THRESHOLD = 5.0; for (Double d : numbers) { if (d >= THRESHOLD) { numbers.remove(d); } } } }
If you compile and run the program you will find that a
ConcurrentModificationException
exception
is thrown. If a collection is modified while an iterator is
visiting the collection, the iterator will probably throw
a ConcurrentModificationException
the next
time next
is invoked.
remove
The only safe way to remove an element from a collection
during a traversal is to use an iterator and invoke its
remove
method:
// Remove 5-9
final double THRESHOLD = 5.0;
Iterator<Double> iter = numbers.iterator();
for (; iter.hasNext(); )
{
if (iter.next() >= THRESHOLD)
{
// Remove the element previously
// returned by next
iter.remove();
}
}
System.out.println(numbers);
Note that you can only invoke remove
once
each time you invoke next
.
Ask the user for 20 or fewer unique integers and compute the median of the integers. Your program should:
import java.util.*; import java.io.PrintStream; public class Median { public static void main(String[] args) { PrintStream output = System.out; Scanner input = new Scanner(System.in); output.println("Enter a list of unique integers separated by spaces"); String sInput = input.nextLine(); StringTokenizer tok = new StringTokenizer(sInput); Set<Integer> numbers = new TreeSet<Integer>(); while (tok.hasMoreTokens()) { int num = Integer.parseInt(tok.nextToken()); if (!numbers.add(num)) { output.printf("%d was not unique%n", num); } } final int SMALLER = numbers.size() / 2; int median = 0; for (Integer i : numbers) { int count = 0; for (Integer j : numbers) { if (j < i) { count++; } } if (count == SMALLER) { median = i; } } output.printf("median of %s is %d%n", numbers.toString(), median); } }
A successfully compiled program can still fail when it is run. Hopefully, such failures are rare occurrences.
The term exception is shorthand for the phrase "exceptional event." An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program's instructions.
The programmer can cause errors to occur by violating preconditions or making logic errors:
String s = "hello"; String t = substring(6); // IndexOutOfBoundsException
/* List<Integer> someIntegers ... */ int sum = 0; for (Integer i : someIntegers) { sum += i; } // ArithmeticException is someIntegers is empty int average = sum / someIntegers.size();
The user can cause errors to occur by entering invalid input:
Enter a number: abc
If the program uses Integer.parseInt
to
convert the string to an integer a NumberFormatException
will occur.
The runtime environment that the program executes in can cause errors to occur.
// FileNotFoundException if a file named // filename is not readable by the user Scanner fileInput = new Scanner(new File(filename));
Similarly, if you try to write a file to a full hard drive
an IOException
will occur.
When the virtual machine detects an invalid operation, it throws an exception. Throwing an exception involves creating an exception object that contains information about the error. The exception object is then handed to the Java runtime system.
When the runtime system receives an exception object, it searches for a block of code called an exception handler that can handle the exception. If such a block of code is found, the exception handler is said to catch the exception.
Thus far, you have not written any exception handlers. If the Java runtime system cannot find an exception handler, the program terminates in an uncontrolled fashion (it crashes).
In Java, you use a try
statement to catch
the exception, and the try
must provide a
handler to deal with the exception.
int num = 0; try { num = Integer.parseInt(someString); } catch (NumberFormatException ex) { output.println("not a number"); }
Finish reading Chapter 11.