Java集合框架官方教程(2):Queue/Deque/Map接口

来源:互联网 发布:unity3d 画线插件 编辑:程序博客网 时间:2024/05/02 00:22

The Queue Interface

    A Queue is a collection for holding elements prior to processing. Besides basic Collection operations, queues provide additional insertion, removal, and inspection operations. TheQueue interface follows.

public interface Queue<E> extends Collection<E> {    E element();    boolean offer(E e);    E peek();    E poll();    E remove();}

    Each Queue method exists in two forms: (1) one throws an exception if the operation fails, and (2) the other returns a special value if the operation fails (eithernull orfalse, depending on the operation). The regular structure of the interface is illustrated inthe following table.

Queue Interface StructureType of OperationThrows exceptionReturns special valueInsertadd(e)offer(e)Removeremove()poll()Examineelement()peek()

    Queues typically, but not necessarily, order elements in a FIFO (first-in-first-out) manner. Among the exceptions are priority queues, which order elements according to their values — see theObject Ordering section for details). Whatever ordering is used, the head of the queue is the element that would be removed by a call to remove orpoll. In a FIFO queue, all new elements are inserted at the tail of the queue. Other kinds of queues may use different placement rules. EveryQueue implementation must specify its ordering properties.

    It is possible for a Queue implementation to restrict the number of elements that it holds; such queues are known asbounded. SomeQueue implementations injava.util.concurrent are bounded, but the implementations injava.util are not.

    The add method, which Queue inherits from Collection, inserts an element unless it would violate the queue's capacity restrictions, in which case it throwsIllegalStateException.Theoffer method, which is intended solely for use on bounded queues, differs fromadd only in that it indicates failure to insert an element by returningfalse.

    The remove and poll methods both remove and return the head of the queue. Exactly which element gets removed is a function of the queue's ordering policy. Theremove andpoll methods differ in their behavior only when the queue is empty. Under these circumstances,remove throwsNoSuchElementException, whilepoll returnsnull.

    The element and peek methods return, but do not remove, the head of the queue. They differ from one another in precisely the same fashion asremove andpoll: If the queue is empty,element throwsNoSuchElementException, whilepeek returnsnull.

   Queue implementations generally do not allow insertion ofnull elements. TheLinkedList implementation, which was retrofitted to implementQueue, is an exception. For historical reasons, it permitsnull elements, but you should refrain from taking advantage of this, becausenull is used as a special return value by the poll and peek methods.

    Queue implementations generally do not define element-based versions of theequals andhashCode methods but instead inherit the identity-based versions fromObject.

    The Queue interface does not define the blocking queue methods, which are common in concurrent programming. These methods, which wait for elements to appear or for space to become available, are defined in the interfacejava.util.concurrent.BlockingQueue, which extends Queue.

    In the following example program, a queue is used to implement a countdown timer. The queue is preloaded with all the integer values from a number specified on the command line to zero, in descending order. Then, the values are removed from the queue and printed at one-second intervals. The program is artificial in that it would be more natural to do the same thing without using a queue, but it illustrates the use of a queue to store elements prior to subsequent processing.

import java.util.*;public class Countdown {    public static void main(String[] args) throws InterruptedException {        int time = Integer.parseInt(args[0]);        Queue<Integer> queue = new LinkedList<Integer>();        for (int i = time; i >= 0; i--)            queue.add(i);        while (!queue.isEmpty()) {            System.out.println(queue.remove());            Thread.sleep(1000);        }    }}
    In the following example, a priority queue is used to sort a collection of elements. Again this program is artificial in that there is no reason to use it in favor of thesort method provided inCollections, but it illustrates the behavior of priority queues.
static <E> List<E> heapSort(Collection<E> c) {    Queue<E> queue = new PriorityQueue<E>(c);    List<E> result = new ArrayList<E>();    while (!queue.isEmpty())        result.add(queue.remove());    return result;}


The Deque Interface


    Usually pronounced as deck, a deque is a double-ended-queue. A double-ended-queue is a linear collection of elements that supports the insertion and removal of elements at both end points.TheDeque interface is a richer abstract data type than bothStack andQueue because it implements both stacks and queues at the same time. TheDeque interface, defines methods to access the elements at both ends of theDeque instance. Methods are provided to insert, remove, and examine the elements. Predefined classes likeArrayDeque and LinkedList implement the Deque interface.

    Note that the Deque interface can be used both as last-in-first-out stacks and first-in-first-out queues.The methods given in theDeque interface are divided into three parts:

Insert

    The addfirst and offerFirst methods insert elements at the beginning of theDeque instance. The methodsaddLast andofferLast insert elements at the end of theDeque instance. When the capacity of theDeque instance is restricted, the preferred methods areofferFirst andofferLast becauseaddFirst might fail to throw an exception if it is full.

Remove

    The removeFirst and pollFirst methods remove elements from the beginning of theDeque instance. TheremoveLast andpollLast methods remove elements from the end. The methodspollFirst andpollLast returnnull if the Deque is empty whereas the methodsremoveFirst andremoveLast throw an exception if theDeque instance is empty.

Retrieve

    The methods getFirst and peekFirst retrieve the first element of theDeque instance.These methods dont remove the value from theDeque instance. Similarly, the methodsgetLastandpeekLast retrieve the last element.The methodsgetFirst andgetLast throw an exception if thedeque instance is empty whereas the methodspeekFirst andpeekLast returnNULL.

    The 12 methods for insertion, removal and retieval of Deque elements are summarized in the following table:

Deque MethodsType of OperationFirst Element (Beginning of the Deque instance)Last Element (End of the Deque instance)InsertaddFirst(e)
offerFirst(e)addLast(e)
offerLast(e)RemoveremoveFirst()
pollFirst()removeLast()
pollLast()ExaminegetFirst()
peekFirst()getLast()
peekLast()

    In addition to these basic methods to insert,remove and examine a Deque instance, theDeque interface also hassome more predefined methods. One of these isremoveFirstOccurence, this method removes the first occurence of the specified element if it exists in theDeque instance. If the element does not exist then theDeque instance remains unchanged. Another similar method isremoveLastOccurence; this method removes the last occurence of the specified element in theDeque instance.The return type of these methods isboolean, and they returntrue if the element exists in theDeque instance.


The Map Interface


    A Map is an object that maps keys to values. A map cannot contain duplicate keys: Each key can map to at most one value.It models the mathematicalfunction abstraction. TheMap interface includes methods for basic operations (such asput,get,remove, containsKey,containsValue,size, andempty),bulk operations (such asputAll andclear), andcollection views (such askeySet,entrySet, andvalues).

    The Java platform contains three general-purposeMap implementations:HashMap,TreeMap, and LinkedHashMap. Their behavior and performance are precisely analogous toHashSet,TreeSet, andLinkedHashSet, as described inThe Set Interface section.

    The remainder of this page discusses the Map interface in detail. But first, here are some more examples of collecting toMaps using JDK 8 aggregate operations. Modeling real-world objects is a common task in object-oriented programming, so it is reasonable to think that some programs might, for example, group employees by department:

// Group employees by departmentMap<Department, List<Employee>> byDept = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment));
    Or compute the sum of all salaries by department:
// Compute sum of salaries by departmentMap<Department, Integer> totalByDept = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment,Collectors.summingInt(Employee::getSalary)));
    Or perhaps group students by passing or failing grades:
// Partition students into passing and failingMap<Boolean, List<Student>> passingFailing = students.stream().collect(Collectors.partitioningBy(s -> s.getGrade()>= PASS_THRESHOLD)); 
    You could also group people by city:
// Classify Person objects by cityMap<String, List<Person>> peopleByCity         = personStream.collect(Collectors.groupingBy(Person::getCity));
    Or even cascade two collectors to classify people by state and city:
// Cascade Collectors Map<String, Map<String, List<Person>>> peopleByStateAndCity  = personStream.collect(Collectors.groupingBy(Person::getState,  Collectors.groupingBy(Person::getCity)))

    Again, these are but a few examples of how to use the new JDK 8 APIs. For in-depthcoverage of lambda expressions and aggregate operations see the lesson entitledAggregate Operations.

Map Interface Basic Operations

    The basic operations of Map (put, get,containsKey,containsValue,size, and isEmpty) behave exactly like their counterparts inHashtable. Thefollowing program generates a frequency table of the words found in its argument list. The frequency table maps each word to the number of times it occurs in the argument list.

import java.util.*;public class Freq {    public static void main(String[] args) {        Map<String, Integer> m = new HashMap<String, Integer>();        // Initialize frequency table from command line        for (String a : args) {            Integer freq = m.get(a);            m.put(a, (freq == null) ? 1 : freq + 1);        }        System.out.println(m.size() + " distinct words:");        System.out.println(m);    }}

    The only tricky thing about this program is the second argument of the put statement. That argument is a conditional expression that has the effect of setting the frequency to one if the word has never been seen before or one more than its current value if the word has already been seen. Try running this program with the command:

java Freq if it is to be it is up to me to delegate
    The program yields the following output.
8 distinct words:{to=3, delegate=1, be=1, it=2, up=1, if=1, me=1, is=2}
Suppose you'd prefer to see the frequency table in alphabetical order. All you have to do is change the implementation type of theMap fromHashMap toTreeMap. Making this four-character change causes the program to generate the following output from the same command line.
8 distinct words:{be=1, delegate=1, if=1, is=2, it=2, me=1, to=3, up=1}
    Similarly, you could make the program print the frequency table in the order the words first appear on the command line simply by changing the implementation type of the map toLinkedHashMap. Doing so results in the following output.
8 distinct words:{if=1, it=2, is=2, to=3, be=1, up=1, me=1, delegate=1}

    This flexibility provides a potent illustration of the power of an interface-based framework.

    Like the Set and List interfaces, Map strengthens the requirements on theequals andhashCode methods so that twoMap objects can be compared for logical equality without regard to their implementation types.TwoMap instances are equal if they represent the same key-value mappings.

    By convention, all general-purpose Map implementations provide constructors that take aMap object and initialize the newMap to contain all the key-value mappings in the specifiedMap. This standardMap conversion constructor is entirely analogous to the standardCollection constructor: It allows the caller to create aMap of a desired implementation type that initially contains all of the mappings in anotherMap, regardless of the otherMap's implementation type. For example, suppose you have aMap, namedm. The following one-liner creates a newHashMap initially containing all of the same key-value mappings asm.

Map<K, V> copy = new HashMap<K, V>(m);

Map Interface Bulk Operations

    The clear operation does exactly what you would think it could do: It removes all the mappings from theMap.TheputAll operation is theMap analogue of theCollection interface'saddAll operation. In addition to its obvious use of dumping oneMap into another, it has a second, more subtle use. Suppose aMap is used to represent a collection of attribute-value pairs; theputAll operation, in combination with the Map conversion constructor, provides a neat way to implement attribute map creation with default values. The following is a static factory method that demonstrates this technique.

static <K, V> Map<K, V> newAttributeMap(Map<K, V>defaults, Map<K, V> overrides) {    Map<K, V> result = new HashMap<K, V>(defaults);    result.putAll(overrides);    return result;}

Collection Views

    The Collection view methods allow a Map to be viewed as aCollection in these three ways:

  • keySet — the Set of keys contained in the Map.
  • values — The Collection of values contained in the Map. This Collection is not aSet, because multiple keys can map to the same value.
  • entrySet — the Set of key-value pairs contained in theMap. TheMap interface provides a small nested interface calledMap.Entry, the type of the elements in thisSet.

    The Collection views provide the only means to iterate over aMap. This example illustrates the standard idiom for iterating over the keys in aMap with afor-each construct:

for (KeyType key : m.keySet())    System.out.println(key);
    and with an iterator:
// Filter a map based on some // property of its keys.for (Iterator<Type> it = m.keySet().iterator(); it.hasNext(); )    if (it.next().isBogus())        it.remove();
The idiom for iterating over values is analogous. Following is the idiom for iterating over key-value pairs.
for (Map.Entry<KeyType, ValType> e : m.entrySet())    System.out.println(e.getKey() + ": " + e.getValue());

    At first, many people worry that these idioms may be slow because the Map has to create a new Collection instance each time a Collection view operation is called. Rest easy: There's no reason that aMap cannot always return the same object each time it is asked for a givenCollection view. This is precisely what all the Map implementations injava.util do.

    With all three Collection views, calling an Iterator'sremove operation removes the associated entry from the backingMap, assuming that the backingMap supports element removal to begin with. This is illustrated by the preceding filtering idiom.

    With the entrySet view, it is also possible to change the value associated with a key by calling aMap.Entry'ssetValue method during iteration (again, assuming theMap supports value modification to begin with).Note that these are the only safe ways to modify aMap during iteration; the behavior is unspecified if the underlyingMap is modified in any other way while the iteration is in progress.

    The Collection views support element removal in all its many forms —remove,removeAll,retainAll, and clear operations, as well as theIterator.remove operation. (Yet again, this assumes that the backingMap supports element removal.)

    The Collection views do not support element addition under any circumstances. It would make no sense for thekeySet andvalues views, and it's unnecessary for theentrySet view, because the backingMap's put andputAll methods provide the same functionality.

Fancy Uses of Collection Views: Map Algebra

    When applied to the Collection views, bulk operations (containsAll,removeAll, andretainAll) are surprisingly potent tools. For starters, supposeyou want to know whether oneMap is a submap of another — that is, whether the firstMap contains all the key-value mappings in the second. The following idiom does the trick.

if (m1.entrySet().containsAll(m2.entrySet())) {    ...}
    Along similar lines, suppose you want to know whether two Map objects contain mappings for all of the same keys.
if (m1.keySet().equals(m2.keySet())) {    ...}
    Suppose you have a Map that represents a collection of attribute-value pairs, and twoSets representing required attributes and permissible attributes. (The permissible attributes include the required attributes.) The following snippet determines whether the attribute map conforms to these constraints and prints a detailed error message if it doesn't.
static <K, V> boolean validate(Map<K, V> attrMap, Set<K> requiredAttrs, Set<K>permittedAttrs) {    boolean valid = true;    Set<K> attrs = attrMap.keySet();    if (! attrs.containsAll(requiredAttrs)) {        Set<K> missing = new HashSet<K>(requiredAttrs);        missing.removeAll(attrs);        System.out.println("Missing attributes: " + missing);        valid = false;    }    if (! permittedAttrs.containsAll(attrs)) {        Set<K> illegal = new HashSet<K>(attrs);        illegal.removeAll(permittedAttrs);        System.out.println("Illegal attributes: " + illegal);        valid = false;    }    return valid;}
    Suppose you want to know all the keys common to two Map objects.
Set<KeyType> commonKeys = new HashSet<KeyType>(m1.keySet());commonKeys.retainAll(m2.keySet());

    A similar idiom gets you the common values.

    All the idioms presented thus far have been nondestructive; that is, they don't modify the backingMap. Here are a few that do. Suppose you want to remove all of the key-value pairs that oneMap has in common with another.

m1.entrySet().removeAll(m2.entrySet());
    Suppose you want to remove from one Map all of the keys that have mappings in another.
m1.keySet().removeAll(m2.keySet());
    What happens when you start mixing keys and values in the same bulk operation? Suppose you have aMap,managers, that maps each employee in a company to the employee's manager. We'll be deliberately vague about the types of the key and the value objects. It doesn't matter, as long as they're the same. Now suppose you want to know who all the "individual contributors" (or nonmanagers) are. The following snippet tells you exactly what you want to know.
Set<Employee> individualContributors = new HashSet<Employee>(managers.keySet());individualContributors.removeAll(managers.values());
    Suppose you want to fire all the employees who report directly to some manager, Simon.
Employee simon = ... ;managers.values().removeAll(Collections.singleton(simon));

    Note that this idiom makes use of Collections.singleton, a static factory method that returns an immutable Set with the single, specified element.

    Once you've done this, you may have a bunch of employees whose managers no longer work for the company (if any of Simon's direct-reports were themselves managers). The following code will tell you which employees have managers who no longer works for the company.

Map<Employee, Employee> m = new HashMap<Employee, Employee>(managers);m.values().removeAll(managers.keySet());Set<Employee> slackers = m.keySet();

    This example is a bit tricky. First, it makes a temporary copy of the Map, and it removes from the temporary copy all entries whose (manager) value is a key in the originalMap. Remember that the originalMap has an entry for each employee. Thus, the remaining entries in the temporaryMap comprise all the entries from the originalMap whose (manager) values are no longer employees. The keys in the temporary copy, then, represent precisely the employees that we're looking for.

    There are many more idioms like the ones contained in this section, but it would be impractical and tedious to list them all. Once you get the hang of it, it's not that difficult to come up with the right one when you need it.

Multimaps

    A multimap is like a Map but it can map each key to multiple values. The Java Collections Framework doesn't include an interface for multimaps because they aren't used all that commonly.It's a fairly simple matter to use a Map whose values areList instances as a multimap. This technique is demonstrated in the next code example, which reads a word list containing one word per line (all lowercase) and prints out all the anagram groups that meet a size criterion. Ananagram group is a bunch of words, all of which contain exactly the same letters but in a different order. The program takes two arguments on the command line: (1) the name of the dictionary file and (2) the minimum size of anagram group to print out. Anagram groups containing fewer words than the specified minimum are not printed.

    There is a standard trick for finding anagram groups: For each word in the dictionary, alphabetize the letters in the word (that is, reorder the word's letters into alphabetical order) and put an entry into a multimap, mapping the alphabetized word to the original word. For example, the word bad causes an entry mapping abd into bad to be put into the multimap. A moment's reflection will show that all the words to which any given key maps form an anagram group. It's a simple matter to iterate over the keys in the multimap, printing out each anagram group that meets the size constraint.

    The following program is a straightforward implementation of this technique.

import java.util.*;import java.io.*;public class Anagrams {    public static void main(String[] args) {        int minGroupSize = Integer.parseInt(args[1]);        // Read words from file and put into a simulated multimap        Map<String, List<String>> m = new HashMap<String, List<String>>();        try {            Scanner s = new Scanner(new File(args[0]));            while (s.hasNext()) {                String word = s.next();                String alpha = alphabetize(word);                List<String> l = m.get(alpha);                if (l == null)                    m.put(alpha, l=new ArrayList<String>());                l.add(word);            }        } catch (IOException e) {            System.err.println(e);            System.exit(1);        }        // Print all permutation groups above size threshold        for (List<String> l : m.values())            if (l.size() >= minGroupSize)                System.out.println(l.size() + ": " + l);    }    private static String alphabetize(String s) {        char[] a = s.toCharArray();        Arrays.sort(a);        return new String(a);    }}
    Running this program on a 173,000-word dictionary file with a minimum anagram group size of eight produces the following output.
9: [estrin, inerts, insert, inters, niters, nitres, sinter,     triens, trines]8: [lapse, leaps, pales, peals, pleas, salep, sepal, spale]8: [aspers, parses, passer, prases, repass, spares, sparse,     spears]10: [least, setal, slate, stale, steal, stela, taels, tales,      teals, tesla]8: [enters, nester, renest, rentes, resent, tenser, ternes,     treens]8: [arles, earls, lares, laser, lears, rales, reals, seral]8: [earings, erasing, gainers, reagins, regains, reginas,     searing, seringa]8: [peris, piers, pries, prise, ripes, speir, spier, spire]12: [apers, apres, asper, pares, parse, pears, prase, presa,      rapes, reaps, spare, spear]11: [alerts, alters, artels, estral, laster, ratels, salter,      slater, staler, stelar, talers]9: [capers, crapes, escarp, pacers, parsec, recaps, scrape,     secpar, spacer]9: [palest, palets, pastel, petals, plates, pleats, septal,     staple, tepals]9: [anestri, antsier, nastier, ratines, retains, retinas,     retsina, stainer, stearin]8: [ates, east, eats, etas, sate, seat, seta, teas]8: [carets, cartes, caster, caters, crates, reacts, recast,     traces]
    Many of these words seem a bit bogus, but that's not the program's fault; they're in the dictionary file. Here's thedictionary file we used.It was derived from the Public Domain ENABLE benchmark reference word list.

Orginal: http://docs.oracle.com/javase/tutorial/collections/interfaces/queue.html

0 0
原创粉丝点击