概述
20.1 Introduction
In object-oriented thinking, a data structure, also known as a container or container object, is an object that stores other objects, referred to as data or elements. To define a data structure is essentially to define a class. The class for a data structure should use data fields to store data and provide methods to support such operations as search, insertion, and deletion. To create a data structure is therefore to create an instance from the class. You can then apply the methods on the instance to manipulate the data structure, such as inserting an element into or deleting an element from the data structure.
20.2 Collections
The Collection interface defines the common operations for lists, vectors, stacks, queues, priority queues, and sets.
The Java Collections Framework supports two types of containers:
■ One for storing a collection of elements is simply called a collection.
■ The other, for storing key/value pairs, is called a map.
Maps are efficient data structures for quickly searching an element using a key. We will introduce maps in the next chapter. Now we turn our attention to the following collections.
■ Sets store a group of nonduplicate elements.
■ Lists store an ordered collection of elements.
■ Stacks store objects that are processed in a last-in, first-out fashion.
■ Queues store objects that are processed in a first-in, first-out fashion.
■ PriorityQueues store objects that are processed in the order of their priorities.
The common features of these collections are defined in the interfaces, and implementations are provided in concrete classes, as shown in Figure 20.1.
Note
All the interfaces and classes defined in the Java Collections Framework are grouped in the java.util package.
Design Guide
The design of the Java Collections Framework is an excellent example of using interfaces, abstract classes, and concrete classes. The interfaces define the framework. The abstract classes provide partial implementation. The concrete classes implement the interfaces with concrete data structures. Providing an abstract class that partially implements an interface makes it convenient for the user to write the code. The user can simply define a concrete class that extends the abstract class rather implements all the methods in the interface. The abstract classes such as AbstractCollection are provided for convenience. For this reason, they are called convenience abstract classes.
The Collection interface is the root interface for manipulating a collection of objects. Its public methods are listed in Figure 20.2. The AbstractCollection class provides partial implementation for the Collection interface. It implements all the methods in Collection except the add, size, and iterator methods. These are implemented in appropriate concrete subclasses.
- The Collection interface provides the basic operations for adding and removing elements in a collection.
- The add method adds an element to the collection.
- The addAll method adds all the elements in the specified collection to this collection.
- The remove method removes an element from the collection.
- The removeAll method removes the elements from this collection that are present in the specified collection.
- The retainAll method retains the elements in this collection that are also present in the specified collection. All these methods return boolean. The return value is true if the collection is changed as a result of the method execution.
- The clear() method simply removes all the elements from the collection.
Note
The methods addAll, removeAll, and retainAll are similar to the set union, difference, and intersection operations.
- The Collection interface provides various query operations.
- The size method returns the number of elements in the collection.
- The contains method checks whether the collection contains the specified element.
- The containsAll method checks whether the collection contains all the elements in the specified collection.
- The isEmpty method returns true if the collection is empty.
- The toArray() method returns an array representation for the collection.
Design Guide
Some of the methods in the Collection interface cannot be implemented in the concrete subclass. In this case, the method would throw java.lang.UnsupportedOperationException, a subclass of RuntimeException. This is a good design that you can use in your project. If a method has no meaning in the subclass, you can implement it as follows:
public void someMethod() {
throw new UnsupportedOperationException
("Method not supported");
}
LISTING 20.1 TestCollection.java
public class TestCollection {
public static void main(String[] args) {
ArrayList<String> collection1 = new ArrayList<>();
collection1.add("New York");
collection1.add("Atlanta");
collection1.add("Dallas");
collection1.add("Madison");
System.out.println("A list of cities in collection1:");
System.out.println(collection1);
System.out.println("nIs Dallas in collection1? " + collection1.contains("Dallas"));
collection1.remove("Dallas");
System.out.println("n" + collection1.size() + " cities are in collection1 now");
Collection<String> collection2 = new ArrayList<>();
collection2.add("Seattle");
collection2.add("Portland");
collection2.add("Los Angeles");
collection2.add("Atlanta");
System.out.println("nA list of cities in collection2:");
System.out.println(collection2);
ArrayList<String> c1 = (ArrayList<String>)(collection1.clone());
c1.addAll(collection2);
System.out.println("nCities in collection1 or collection2: ");
System.out.println(c1);
c1 = (ArrayList<String>)(collection1.clone());
c1.retainAll(collection2);
System.out.print("nCities in collection1 and collection2: ");
System.out.println(c1);
c1 = (ArrayList<String>)(collection1.clone());
c1.removeAll(collection2);
System.out.print("nCities in collection1, but not in 2: ");
System.out.println(c1);
}
}
A list of cities in collection1:
[New York, Atlanta, Dallas, Madison]
Is Dallas in collection1? true
3 cities are in collection1 now
A list of cities in collection2:
[Seattle, Portland, Los Angeles, Atlanta]
Cities in collection1 or collection2:
[New York, Atlanta, Madison, Seattle, Portland, Los Angeles, Atlanta]
Cities in collection1 and collection2: [Atlanta]
Cities in collection1, but not in 2: [New York, Madison]
Note
All the concrete classes in the Java Collections Framework implement the java.lang. Cloneable and java.io.Serializable interfaces except that java.util.PriorityQueue does not implement the Cloneable interface. Thus, all instances of Cloneable except priority queues can be cloned and all instances of Cloneable can be serialized.
20.3 Iterators
Each collection is Iterable. You can obtain its Iterator object to traverse all the elements in the collection.
Iterator is a classic design pattern for walking through a data structure without having to expose the details of how data is stored in the data structure.
The Collection interface extends the Iterable interface. The Iterable interface defines the iterator method, which returns an iterator. The Iterator interface provides a uniform way for traversing elements in various types of collections. The iterator() method in the Iterable interface returns an instance of Iterator, as shown in Figure 20.2, which provides sequential access to the elements in the collection using the next() method. You can also use the hasNext() method to check whether there are more elements in the iterator, and the remove() method to remove the last element returned by the iterator.
LISTING 20.2 TestIterator.java
import java.util.*;
public class TestIterator {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<>();
collection.add("New York");
collection.add("Atlanta");
collection.add("Dallas");
collection.add("Madison");
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next().toUpperCase() + " ");
}
System.out.println();
}
}
NEW YORK ATLANTA DALLAS MADISON
Tip
You can simplify the code in lines 11–14 using a foreach loop without using an iterator, as follows:
for (String element: collection)
System.out.print(element.toUpperCase() + " ");
This loop is read as “for each element in the collection, do the following.” The foreach loop can be used for arrays (see Section 7.2.7) as well as any instance of Iterable.
20.4 Lists
The List interface extends the Collection interface and defines a collection for storing elements in a sequential order. To create a list, use one of its two concrete classes: ArrayList or LinkedList.
20.4.1 The Common Methods in the List Interface
ArrayList and LinkedList are defined under the List interface. The List interface extends Collection to define an ordered collection with duplicates allowed. The List interface adds position-oriented operations, as well as a new list iterator that enables the user to traverse the list bidirectionally.
- The add(index, element) method is used to insert an element at a specified index.
- The addAll(index, collection) method to insert a collection of elements at a specified index.
- The remove(index) method is used to remove an element at the specified index from the list.
- A new element can be set at the specified index using the set(index, element) method.
- The indexOf(element) method is used to obtain the index of the specified element’s first occurrence in the list.
- The lastIndexOf(element) method to obtain the index of its last occurrence.
- A sublist can be obtained by using the subList(fromIndex, toIndex) method.
The listIterator() or listIterator(startIndex) method returns an instance of ListIterator. The ListIterator interface extends the Iterator interface to add bidirectional traversal of the list. The methods in ListIterator are listed in Figure 20.4.
- The hasNext() method defined in the Iterator interface is used to check whether the iterator has more elements when traversed in the forward direction.
- The hasPrevious() method to check whether the iterator has more elements when traversed in the backward direction.
- The next() method defined in the Iterator interface returns the next element in the iterator.
- The previous() method returns the previous element in the iterator.
- The nextIndex() method returns the index of the next element in the iterator.
- The previousIndex() returns the index of the previous element in the iterator.
- The AbstractList class provides a partial implementation for the List interface.
- The AbstractSequentialList class extends AbstractList to provide support for linked lists.
20.4.2 The ArrayList and LinkedList Classes
The ArrayList class and the LinkedList class are two concrete implementations of the List interface. ArrayList stores elements in an array. The array is dynamically created. If the capacity of the array is exceeded, a larger new array is created and all the elements from the current array are copied to the new array.
LinkedList stores elements in a linked list. Which of the two classes you use depends on your specific needs.
ArrayList or LinkedList?
If you need to support random access through an index without inserting or removing elements at the beginning of the list, ArrayList offers the most efficient collection. If, however, your application requires the insertion or deletion of elements at the beginning of the list, you should choose LinkedList.
A list can grow or shrink dynamically. Once it is created, an array is fixed. If your application does not require the insertion or deletion of elements, an array is the most efficient data structure.
ArrayList is a resizable-array implementation of the List interface. It also provides methods for manipulating the size of the array used internally to store the list, as shown in Figure 20.5.
Each ArrayList instance has a capacity, which is the size of the array used to store the elements in the list. It is always at least as large as the list size. As elements are added to an ArrayList, its capacity grows automatically. An ArrayList does not automatically shrink. You can use the trimToSize() method to reduce the array capacity to the size of the list.
An ArrayList can be constructed using its no-arg constructor, ArrayList(Collection), or ArrayList(initialCapacity).
LinkedList is a linked list implementation of the List interface. In addition to implementing the List interface, this class provides the methods for retrieving, inserting, and removing elements from both ends of the list, as shown in Figure 20.6.
A LinkedList can be constructed using its no-arg constructor or LinkedList(Collection).
LISTING 20.3 TestArrayAndLinkedList.java
import java.util.*;
public class TestArrayAndLinkedList {
public static void main(String[] args) {
List<Integer> arrayList = new ArrayList<>();
arrayList.add(1); // 1 is autoboxed to new Integer(1)
arrayList.add(2);
arrayList.add(3);
arrayList.add(1);
arrayList.add(4);
arrayList.add(0, 10);
arrayList.add(3, 30);
System.out.println("A list of integers in the array list:");
System.out.println(arrayList);
LinkedList<Object> linkedList = new LinkedList<>(arrayList);
linkedList.add(1, "red");
linkedList.removeLast();
linkedList.addFirst("green");
System.out.println("Display the linked list forward:");
ListIterator<Object> listIterator = linkedList.listIterator();
while (listIterator.hasNext()) {
System.out.print(listIterator.next() + " ");
}
System.out.println();
System.out.println("Display the linked list backward:");
listIterator = linkedList.listIterator(linkedList.size());
while (listIterator.hasPrevious()) {
System.out.print(listIterator.previous() + " ");
}
}
}
A list of integers in the array list:
[10, 1, 2, 30, 3, 1, 4]
Display the linked list forward:
green 10 red 1 2 30 3 1
Display the linked list backward:
1 3 30 2 1 red 10 green
A list can hold identical elements. Integer 1 is stored twice in the list (lines 6, 9). ArrayList and LinkedList operate similarly. The critical difference between them pertains to internal implementation, which affects their performance. ArrayList is efficient for retrieving elements and LinkedList is efficient for inserting and removing elements at the beginning of the list. Both have the same performance for inserting and removing elements in the middle or at the end of the list.
The get(i) method is available for a linked list, but it is a time-consuming operation. Do not use it to traverse all the elements in a list as shown in (a). Instead you should use an iterator as shown in (b). Note that a foreach loop uses an iterator implicitly.
Tip
Java provides the static asList method for creating a list from a variable-length list of arguments. Thus you can use the following code to create a list of strings and a list of integers:
List<String> list1 = Arrays.asList("red", "green", "blue");
List<Integer> list2 = Arrays.asList(10, 20, 30, 40, 50);
20.5 The Comparator Interface
Comparator can be used to compare the objects of a class that doesn’t implement Comparable.
You can define a comparator to compare the elements of different classes. To do so, define a class that implements the java.util.Comparator<T> interface and overrides its compare method.
public int compare(T element1, T element2)
Returns a negative value if element1 is less than element2, a positive value if element1 is greater than element2, and zero if they are equal.
LISTING 20.4 GeometricObjectComparator.java
import java.util.Comparator;
public class GeometricObjectComparator
implements Comparator<GeometricObject>, java.io.Serializable {
public int compare(GeometricObject o1, GeometricObject o2) {
double area1 = o1.getArea();
double area2 = o2.getArea();
if (area1 < area2)
return -1;
else if (area1 == area2)
return 0;
else
return 1;
}
}
LISTING 20.5 TestComparator.java
import java.util.Comparator;
public class TestComparator {
public static void main(String[] args) {
GeometricObject g1 = new Rectangle(5, 5);
GeometricObject g2 = new Circle(5);
GeometricObject g = max(g1, g2, new GeometricObjectComparator());
System.out.println("The area of the larger object is " +g.getArea());
}
public static GeometricObject max(GeometricObject g1, GeometricObject g2, Comparator<GeometricObject> c) {
if (c.compare(g1, g2) > 0)
return g1;
else
return g2;
}
}
The area of the larger object is 78.53981633974483
The Rectangle and Circle classes were defined in Section 13.2, Abstract Classes.The GeometricObjectComparator is created and passed to the max method and this comparator is used in the max method to compare the geometric objects.
Note
Comparable is used to compare the objects of the class that implement Comparable. Comparator can be used to compare the objects of a class that doesn’t implement Comparable.
Comparing elements using the Comparable interface is referred to as comparing using natural order, and comparing elements using the Comparator interface is referred to as comparing using comparator.
20.6 Static Methods for Lists and Collections
The Collections class contains static methods to perform common operations in a collection and a list.
You can sort the comparable elements in a list in its natural order with the compareTo method in the Comparable interface. You may also specify a comparator to sort elements. For example, the following code sorts strings in a list.
List<String> list = Arrays.asList("red", "green", "blue");
Collections.sort(list);
System.out.println(list);
The output is [blue, green, red].
The preceding code sorts a list in ascending order. To sort it in descending order, you can simply use the Collections.reverseOrder() method to return a Comparator object that orders the elements in reverse of their natural order. For example, the following code sorts a list of strings in descending order.
List<String> list = Arrays.asList("yellow", "red", "green", "blue");
Collections.sort(list, Collections.reverseOrder());
System.out.println(list);
The output is [yellow, red, green, blue].
You can use the binarySearch method to search for a key in a list. To use this method, the list must be sorted in increasing order. If the key is not in the list, the method returns -(insertion point +1). Recall that the insertion point is where the item would fall in the list if it were present. For example, the following code searches the keys in a list of integers and a list of strings.
List<Integer> list1 = Arrays.asList(2, 4, 7, 10, 11, 45, 50, 59, 60, 66);
System.out.println("(1) Index: " + Collections.binarySearch(list1, 7));
System.out.println("(2) Index: " + Collections.binarySearch(list1, 9));
List<String> list2 = Arrays.asList("blue", "green", "red");
System.out.println("(3) Index: " + Collections.binarySearch(list2, "red"));
System.out.println("(4) Index: " + Collections.binarySearch(list2, "cyan"));
The output of the preceding code is:
(1) Index: 2
(2) Index: -4
(3) Index: 2
(4) Index: -2
You can use the reverse method to reverse the elements in a list. For example, the following code displays [blue, green, red, yellow].
List<String> list = Arrays.asList("yellow", "red", "green", "blue");
Collections.reverse(list);
System.out.println(list);
You can use the shuffle(List) method to randomly reorder the elements in a list. For example, the following code shuffles the elements in list.
List<String> list = Arrays.asList("yellow", "red", "green", "blue");
Collections.shuffle(list);
System.out.println(list);
You can also use the shuffle(List, Random) method to randomly reorder the elements in a list with a specified Random object. Using a specified Random object is useful to generate a list with identical sequences of elements for the same original list. For example, the following code shuffles the elements in list.
List<String> list1 = Arrays.asList("yellow", "red", "green", "blue");
List<String> list2 = Arrays.asList("yellow", "red", "green", "blue");
Collections.shuffle(list1, new Random(20));
Collections.shuffle(list2, new Random(20));
System.out.println(list1);
System.out.println(list2);
You will see that list1 and list2 have the same sequence of elements before and after the shuffling.
You can use the copy(det, src) method to copy all the elements from a source list to a destination list on the same index. The destination list must be as long as the source list. If it is longer, the remaining elements in the source list are not affected. For example, the following code copies list2 to list1.
List<String> list1 = Arrays.asList("yellow", "red", "green", "blue");
List<String> list2 = Arrays.asList("white", "black");
Collections.copy(list1, list2);
System.out.println(list1);
The output for list1 is [white, black, green, blue]. The copy method performs a shallow copy: only the references of the elements from the source list are copied.
You can use the nCopies(int n, Object o) method to create an immutable list that consists of n copies of the specified object. For example, the following code creates a list with five Calendar objects.
List<GregorianCalendar> list1 = Collections.nCopies (5, new GregorianCalendar(2005, 0, 1));
The list created from the nCopies method is immutable, so you cannot add, remove, or update elements in the list. All the elements have the same references.
You can use the fill(List list, Object o) method to replace all the elements in the list with the specified element. For example, the following code displays [black, black, black].
List<String> list = Arrays.asList("red", "green", "blue");
Collections.fill(list, "black");
System.out.println(list);
You can use the max and min methods for finding the maximum and minimum elements in a collection. The elements must be comparable using the Comparable interface or the Comparator interface. For example, the following code displays the largest and smallest strings in a collection.
Collection<String> collection = Arrays.asList("red", "green", "blue");
System.out.println(Collections.max(collection));
System.out.println(Collections.min(collection));
The disjoint(collection1, collection2) method returns true if the two collections have no elements in common. For example, in the following code, disjoint(collection1, collection2) returns false, but disjoint(collection1, collection3) returns true.
Collection<String> collection1 = Arrays.asList("red", "cyan");
Collection<String> collection2 = Arrays.asList("red", "blue");
Collection<String> collection3 = Arrays.asList("pink", "tan");
System.out.println(Collections.disjoint(collection1, collection2));
System.out.println(Collections.disjoint(collection1, collection3));
The frequency(collection, element) method finds the number of occurrences of the element in the collection. For example, frequency(collection, "red") returns 2 in the following code.
Collection<String> collection = Arrays.asList("red", "cyan", "red");
System.out.println(Collections.frequency(collection, "red"));
20.7 Case Study: Bouncing Balls
This section presents a program that displays bouncing balls and enables the user to add and remove balls.
You can use two buttons to suspend and resume the movement of the balls, a scroll bar to control the ball speed, and the + or - button add or remove a ball, as shown in Figure 20.8.
LISTING 20.6 MultipleBounceBall.java
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollBar;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.util.Duration;
public class MultipleBounceBall extends Application {
@Override // Override the start method in the Application class
public void start(Stage primaryStage) {
MultipleBallPane ballPane = new MultipleBallPane();
ballPane.setStyle("-fx-border-color: yellow");
Button btAdd = new Button("+");
Button btSubtract = new Button("-");
HBox hBox = new HBox(10);
hBox.getChildren().addAll(btAdd, btSubtract);
hBox.setAlignment(Pos.CENTER);
// Add or remove a ball
btAdd.setOnAction(e -> ballPane.add());
btSubtract.setOnAction(e -> ballPane.subtract());
// Pause and resume animation
ballPane.setOnMousePressed(e -> ballPane.pause());
ballPane.setOnMouseReleased(e -> ballPane.play());
// Use a scroll bar to control animation speed
ScrollBar sbSpeed = new ScrollBar();
sbSpeed.setMax(20);
sbSpeed.setValue(10);
ballPane.rateProperty().bind(sbSpeed.valueProperty());
BorderPane pane = new BorderPane();
pane.setCenter(ballPane);
pane.setTop(sbSpeed);
pane.setBottom(hBox);
// Create a scene and place the pane in the stage
Scene scene = new Scene(pane, 250, 150);
primaryStage.setTitle("MultipleBounceBall"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
}
private class MultipleBallPane extends Pane {
private Timeline animation;
public MultipleBallPane() {
// Create an animation for moving the ball
animation = new Timeline(new KeyFrame(Duration.millis(50), e -> moveBall()));
animation.setCycleCount(Timeline.INDEFINITE);
animation.play(); // Start animation
}
public void add() {
Color color = new Color(Math.random(), Math.random(), Math.random(), 0.5);
getChildren().add(new Ball(30, 30, 20, color));
}
public void subtract() {
if (getChildren().size() > 0) {
getChildren().remove(getChildren().size() - 1);
}
}
public void play() {
animation.play();
}
public void pause() {
animation.pause();
}
public void increaseSpeed() {
animation.setRate(animation.getRate() + 0.1);
}
public void decreaseSpeed() {
animation.setRate(animation.getRate() > 0 ? animation.getRate() - 0.1 : 0);
}
public DoubleProperty rateProperty() {
return animation.rateProperty();
}
protected void moveBall() {
for (Node node: this.getChildren()) {
Ball ball = (Ball)node;
// Check boundaries
if (ball.getCenterX() < ball.getRadius() || ball.getCenterX() > getWidth() - ball.getRadius()) {
ball.dx *= -1; // Change ball move direction
}
if (ball.getCenterY() < ball.getRadius() || ball.getCenterY() > getHeight() - ball.getRadius()) {
ball.dy *= -1; // Change ball move direction
}
// Adjust ball position
ball.setCenterX(ball.dx + ball.getCenterX());
ball.setCenterY(ball.dy + ball.getCenterY());
}
}
}
class Ball extends Circle {
private double dx = 1, dy = 1;
Ball(double x, double y, double radius, Color color) {
super(x, y, radius);
setFill(color); // Set ball color
}
}
}
20.8 Vector and Stack Classes
Vector is a subclass of AbstractList, and Stack is a subclass of Vector in the Java API.
Vector is the same as ArrayList, except that it contains synchronized methods for accessing and modifying the vector. Synchronized methods can prevent data corruption when a vector is accessed and modified by two or more threads concurrently. For the many applications that do not require synchronization, using ArrayList is more efficient than using Vector.
The Vector class extends the AbstractList class. It also has the methods contained in the original Vector class defined prior to Java 2, as shown in Figure 20.10.
Most of the methods in the Vector class listed in the UML diagram in Figure 20.10 are similar to the methods in the List interface. These methods were introduced before the Java Collections Framework. For example, addElement(Object element) is the same as the add(Object element) method, except that the addElement method is synchronized. Use the ArrayList class if you don’t need synchronization. It works much faster than Vector.
Note
The elements() method returns an Enumeration. The Enumeration interface was introduced prior to Java 2 and was superseded by the Iterator interface.
Note
Vector is widely used in Java legacy code because it was the Java resizable array implementation before Java 2. In the Java Collections Framework, Stack is implemented as an extension of Vector, as illustrated in Figure 20.11.
20.9 Queues and Priority Queues
In a priority queue, the element with the highest priority is removed first.
In a priority queue, elements are assigned priorities. When accessing elements, the element with the highest priority is removed first.
20.9.1 The Queue Interface
The Queue interface extends java.util.Collection with additional insertion, extraction, and inspection operations, as shown in Figure 20.12.
20.9.2 Deque and LinkedList
The LinkedList class implements the Deque interface, which extends the Queue interface, as shown in Figure 20.13. Therefore, you can use LinkedList to create a queue. LinkedList is ideal for queue operations because it is efficient for inserting and removing elements from both ends of a list.
Deque supports element insertion and removal at both ends. The name deque is short for “double-ended queue” and is usually pronounced “deck.” The Deque interface extends Queue with additional methods for inserting and removing elements from both ends of the queue. The methods addFirst(e), removeFirst(), addLast(e), removeLast(), getFirst(), and getLast() are defined in the Deque interface.
LISTING 20.7 TestQueue.java
public class TestQueue {
public static void main(String[] args) {
java.util.Queue<String> queue = new java.util.LinkedList<>();
queue.offer("Oklahoma");
queue.offer("Indiana");
queue.offer("Georgia");
queue.offer("Texas");
while (queue.size() > 0)
System.out.print(queue.remove() + " ");
}
}
Oklahoma Indiana Georgia Texas
The PriorityQueue class implements a priority queue, as shown in Figure 20.14. By default, the priority queue orders its elements according to their natural ordering using Comparable. The element with the least value is assigned the highest priority and thus is removed from the queue first. If there are several elements with the same highest priority, the tie is broken arbitrarily. You can also specify an ordering using Comparator in the constructor PriorityQueue(initialCapacity, comparator).
LISTING 20.8 PriorityQueueDemo.java
import java.util.*;
public class PriorityQueueDemo {
public static void main(String[] args) {
PriorityQueue<String> queue1 = new PriorityQueue<>();
queue1.offer("Oklahoma");
queue1.offer("Indiana");
queue1.offer("Georgia");
queue1.offer("Texas");
System.out.println("Priority queue using Comparable:");
while (queue1.size() > 0) {
System.out.print(queue1.remove() + " ");
}
PriorityQueue<String> queue2 = new PriorityQueue(4, Collections.reverseOrder());
queue2.offer("Oklahoma");
queue2.offer("Indiana");
queue2.offer("Georgia");
queue2.offer("Texas");
System.out.println("nPriority queue using Comparator:");
while (queue2.size() > 0) {
System.out.print(queue2.remove() + " ");
}
}
}
Priority queue using Comparable:
Georgia Indiana Oklahoma Texas
Priority queue using Comparator:
Texas Oklahoma Indiana Georgia
20.10 Case Study: Evaluating Expressions
Stacks can be used to evaluate expressions.
The problem can be solved using two stacks, named operandStack and operatorStack, for storing operands and operators, respectively. Operands and operators are pushed into the stacks before they are processed. When an operator is processed, it is popped from operatorStack and applied to the first two operands from operandStack (the two operands are popped from operandStack). The resultant value is pushed back to operandStack. The algorithm proceeds in two phases:
Phase 1: Scanning the expression
The program scans the expression from left to right to extract operands, operators, and the parentheses.
1.1. If the extracted item is an operand, push it to operandStack.
1.2. If the extracted item is a + or - operator, process all the operators at the top of operatorStack and push the extracted operator to operatorStack.
1.3. If the extracted item is a * or / operator, process the * or / operators at the top of operatorStack and push the extracted operator to operatorStack.
1.4. If the extracted item is a ( symbol, push it to operatorStack.
1.5. If the extracted item is a ) symbol, repeatedly process the operators from the top of operatorStack until seeing the ( symbol on the stack.
Phase 2: Clearing the stack
Repeatedly process the operators from the top of operatorStack until operatorStack is empty.
Table 20.1 shows how the algorithm is applied to evaluate the expression (1 + 2) * 4 - 3.
LISTING 20.9 EvaluateExpression.java
import java.util.Stack;
public class EvaluateExpression {
public static void main(String[] args) {
// Check number of arguments passed
if (args.length != 1) {
System.out.println("Usage: java EvaluateExpression "expression"");
System.exit(1);
}
try {
System.out.println(evaluateExpression(args[0]));
}
catch (Exception ex) {
System.out.println("Wrong expression: " + args[0]);
}
}
/** Evaluate an expression */
public static int evaluateExpression(String expression) {
// Create operandStack to store operands
Stack<Integer> operandStack = new Stack<>();
// Create operatorStack to store operators
Stack<Character> operatorStack = new Stack<>();
// Insert blanks around (, ), +, -, /, and *
expression = insertBlanks(expression);
// Extract operands and operators
String[] tokens = expression.split(" ");
// Phase 1: Scan tokens
for (String token: tokens) {
if (token.length() == 0) // Blank space
continue; // Back to the while loop to extract the next token
else if (token.charAt(0) == '+' || token.charAt(0) == '-') {
// Process all +, -, *, / in the top of the operator stack
while (!operatorStack.isEmpty() &&
(operatorStack.peek() == '+' ||
operatorStack.peek() == '-' ||
operatorStack.peek() == '*' ||
operatorStack.peek() == '/')) {
processAnOperator(operandStack, operatorStack);
}
// Push the + or - operator into the operator stack
operatorStack.push(token.charAt(0));
}
else if (token.charAt(0) == '*' || token.charAt(0) == '/') {
// Process all *, / in the top of the operator stack
while (!operatorStack.isEmpty() &&
(operatorStack.peek() == '*' ||
operatorStack.peek() == '/')) {
processAnOperator(operandStack, operatorStack);
}
// Push the * or / operator into the operator stack
operatorStack.push(token.charAt(0));
}
else if (token.trim().charAt(0) == '(') {
operatorStack.push('('); // Push '(' to stack
}
else if (token.trim().charAt(0) == ')') {
// Process all the operators in the stack until seeing '('
while (operatorStack.peek() != '(') {
processAnOperator(operandStack, operatorStack);
}
operatorStack.pop(); // Pop the '(' symbol from the stack
}
else { // An operand scanned
// Push an operand to the stack
operandStack.push(new Integer(token));
}
}
// Phase 2: Process all the remaining operators in the stack
while (!operatorStack.isEmpty()) {
processAnOperator(operandStack, operatorStack);
}
// Return the result
return operandStack.pop();
}
/** Process one operator: Take an operator from operatorStack and
* apply it on the operands in the operandStack */
public static void processAnOperator(
Stack<Integer> operandStack, Stack<Character> operatorStack) {
char op = operatorStack.pop();
int op1 = operandStack.pop();
int op2 = operandStack.pop();
if (op == '+')
operandStack.push(op2 + op1);
else if (op == '-')
operandStack.push(op2 - op1);
else if (op == '*')
operandStack.push(op2 * op1);
else if (op == '/')
operandStack.push(op2 / op1);
}
public static String insertBlanks(String s) {
String result = "";
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(' || s.charAt(i) == ')' ||
s.charAt(i) == '+' || s.charAt(i) == '-' ||
s.charAt(i) == '*' || s.charAt(i) == '/')
result += " " + s.charAt(i) + " ";
else
result += s.charAt(i);
}
return result;
}
}
CHAPTER 20 SUMMARY
1. The Java Collections Framework supports sets, lists, queues, and maps. They are defined in the interfaces Set, List, Queue, and Map.
2. A list stores an ordered collection of elements.
3. All the concrete classes except PriorityQueue in the Java Collections Framework implement the Cloneable and Serializable interfaces. Thus, their instances can be cloned and serialized.
4. To allow duplicate elements to be stored in a collection, you need to use a list. A list not only can store duplicate elements but also allows the user to specify where they are stored. The user can access elements by an index.
5. Two types of lists are supported: ArrayList and LinkedList. ArrayList is a resizable-array implementation of the List interface. All the methods in ArrayList are defined in List. LinkedList is a linked-list implementation of the List interface. In addition to implementing the List interface, this class provides the methods for retrieving, inserting, and removing elements from both ends of the list.
6. Comparator can be used to compare the objects of a class that doesn’t implement Comparable.
7. The Vector class extends the AbstractList class. Starting with Java 2, Vector has been the same as ArrayList, except that the methods for accessing and modifying the vector are synchronized. The Stack class extends the Vector class and provides several methods for manipulating the stack.
8. The Queue interface represents a queue. The PriorityQueue class implements Queue for a priority queue.
最后
以上就是震动手套为你收集整理的Java学习笔记(20) Lists, Stacks, Queues, and Priority Queues20.1 Introduction20.2 Collections20.3 Iterators20.4 Lists20.5 The Comparator Interface20.6 Static Methods for Lists and Collections20.7 Case Study: Bouncing Balls20.8 Vector and Stack Classes20.9的全部内容,希望文章能够帮你解决Java学习笔记(20) Lists, Stacks, Queues, and Priority Queues20.1 Introduction20.2 Collections20.3 Iterators20.4 Lists20.5 The Comparator Interface20.6 Static Methods for Lists and Collections20.7 Case Study: Bouncing Balls20.8 Vector and Stack Classes20.9所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复