The java.utilpackage provides the interfaces and classes that form the collections framework. I introduce collections-oriented interfaces and classes that also support con- currency later in this chapter, in the “New and Improved Concurrency” section. Table 2-5 describes the six new interfaces and classes that Java SE 6 integrates into this package.
Method Description
Table 2-5.New java.util Package Interfaces and Classes
Interface/Class Description
Deque<E> An interface that describes a double-ended queue, a linear collection that supports the insertion and removal of elements at either end.
NavigableMap<K, V> An interface that describes an extended SortedMap<K, V>with navigation methods that return the closest matches for specific search targets.
NavigableSet<E> An interface that describes an extended
SortedSet<E>with navigation methods that return the closest matches for specific search targets.
AbstractMap.SimpleEntry<K, V> A class that implements a mutable Map.Entry<K, V>, maintaining a key and a value.
AbstractMap.SimpleImmutableEntry<K, V> A class that implements an immutable
Map.Entry<K, V>, maintaining a key and a value.
ArrayDeque<E> A class that implements Deque<E>as a resizable array. It allows efficient insertion and removal of elements at both ends, and is a great choice for stacks or queues.
The Deque<E>interface and ArrayDeque<E>implementation class are preferable to the legacy java.util.Stack<E>class when introducing a stack data structure into source code.
The fact that Stack<E>is implemented as a java.util.Vector<E>is one reason for this pref- erence. This implementation makes it easy to access Vector<E>methods that can violate the integrity of the stack, such as public void add(int index, E element). To use a deque as a stack, Deque<E>provides void addFirst(E e), E removeFirst(), and E peekFirst() methods. These methods correspond to the Stack<E>class’s E push(E item), E pop(), and E peek()methods.
One application that benefits from a stack is a postfix calculator, which requires an operator’s operands to be specified before the operator. For example, 10.5 30.2 +is a postfix expression that sums 10.5 and 30.2. The source code for a postfix calculator application that uses Deque<E>and ArrayDeque<E>for its stack is presented in Listing 2-8.
Listing 2-8.PostfixCalc.java // PostfixCalc.java import java.io.*;
import java.util.*;
public class PostfixCalc {
public static void main (String [] args) throws IOError {
Console console = System.console ();
if (console == null) {
System.err.println ("unable to obtain console");
return;
}
console.printf ("Postfix expression Calculator\n\n");
console.printf ("Valid operators: + - * /\n");
console.printf ("Valid commands: c/C (clear stack), "+
"t/t (view stack top)\n\n");
Deque<Double> stack = new ArrayDeque<Double> ();
loop:
while (true) {
String line = console.readLine (">").trim ();
switch (line.charAt (0)) {
case 'Q':
case 'q': break loop;
case 'C':
case 'c': while (stack.peekFirst () != null) stack.removeFirst ();
break;
case 'T':
case 't': console.printf ("%f\n", stack.peekFirst ());
break;
case '+': if (stack.size () < 2) {
console.printf ("missing operand\n");
break;
}
double op2 = stack.removeFirst ();
double op1 = stack.removeFirst ();
double res = op1+op2;
console.printf ("%f+%f=%f\n", op1, op2, res);
stack.addFirst (res);
break;
case '-': if (stack.size () < 2) {
console.printf ("missing operand\n");
break;
}
op2 = stack.removeFirst ();
op1 = stack.removeFirst ();
res = op1-op2;
console.printf ("%f-%f=%f\n", op1, op2, res);
stack.addFirst (res);
break;
case '*': if (stack.size () < 2) {
console.printf ("missing operand\n");
break;
}
op2 = stack.removeFirst ();
op1 = stack.removeFirst ();
res = op1*op2;
console.printf ("%f*%f=%f\n", op1, op2, res);
stack.addFirst (res);
break;
case '/': if (stack.size () < 2) {
console.printf ("missing operand\n");
break;
}
op2 = stack.removeFirst ();
op1 = stack.removeFirst ();
res = op1/op2;
console.printf ("%f/%f=%f\n", op1, op2, res);
stack.addFirst (res);
break;
default : try {
stack.addFirst (Double.parseDouble (line));
}
catch (NumberFormatException nfe) {
console.printf ("double value expected\n");
} }
} } }
When you run this application, you will be prompted to enter a line of input. Enter an operator (+, -, *, or /), a number operand, or a command (c/C, t/T, or q/Q), but only one of these items. Before entering an operator, remember that you need at least two operands on the stack. Here is an example:
>10
>20
>+
10.000000+20.000000=30.000000
>q
■ Note The LinkedList<E>class has been reworked to implement Deque<E>.
The NavigableMap<K, V>and NavigableSet<E>interfaces provide methods that return a map view based on a range of keys and a set view based on a range of entries. For example, because the TreeMap<K, V>and TreeSet<E>classes have been retrofitted to implement these interfaces, the following TreeMap<K, V>method returns a TreeMap<K, V>-backed view that presents a range of the map’s keys:
public NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)
and the following TreeSet<E>method returns a TreeMap<E>-backed view that presents a range of the set’s entries:
public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive)
Listing 2-9 presents the source code to a product database application that demonstrates subMap()and two more new TreeMap<K, V>methods:
• SortedMap<K,V> headMap(K toKey), which returns a map view whose keys are less than toKey
• SortedMap<K,V> tailMap(K fromKey), which returns a map view whose keys are greater than or equal to fromKey
Listing 2-9.ProductDB.java //ProductDB.java
import java.util.*;
import java.util.Map;
public class ProductDB {
public static void build (Map<Integer, Product> map) {
map.put (1000, new Product ("DVD player", 350));
map.put (1011, new Product ("10 kilo bag of potatoes", 15.75));
map.put (1102, new Product ("Magazine", 8.50));
map.put (2023, new Product ("Automobile", 18500));
map.put (2034, new Product ("Towel", 9.99));
}
public static void main(String[] args) {
TreeMap<Integer, Product> db = new TreeMap<Integer, Product> ();
build (db);
System.out.println ("Database view of products ranging from 1000-1999");
System.out.println (db.subMap (1000, 1999)+"\n");
System.out.println ("Database view of products >= 1011");
System.out.println (db.tailMap (1011)+"\n");
System.out.println ("Database view of products < 2023");
System.out.println (db.headMap (2023));
} }
class Product {
String desc;
double price;
Product (String desc, double price) {
this.desc = desc;
this.price = price;
}
public String toString () {
return "Description="+desc+", Price="+price;
} }
When you run this application, you will discover the following:
• db.subMap (1000, 1999)returns a view that identifies products whose keys are 1000, 1011, and 1102.
• db.tailMap (1011)returns a view that identifies products whose keys are 1011, 1102, 2023, and 2034.
• db.headMap (2023)returns a view that identifies products whose keys are 1000, 1011, and 1102.
■ Note Check out Java Boutique’s “SortedSet and SortedMap Made Easier with Two New Mustang Interfaces” article (http://javaboutique.internet.com/tutorials/mustang/index.html) for a complete look at the NavigableMap<K,V>and NavigableSet<E>methods.