将对象映射到其他对象的能力是一种解决编程问题的杀手锏。例如,考虑一个程序,它将用来检查Java的Random类的随机性。理想状态下,Random可以将产生理想的数字分布,但要想测试它,则需要生成大量的随机数,并对各种落入不同范围的数字进行计数。Map可以很容易地解决该问题。在本例中,键是由Random产生的数字,而值是该数字出现的次数: 1import java.util.HashMap; 2import java.util.Map; 3import java.util.Random; 4public class Statistics { 5 public static void main(String[] args) { 6 Random rand = new Random(47); 7 Map<Integer, Integer> m = new HashMap<Integer, Integer>(); 8 for (int i = 0; i < 1000; i ) { 9 int r = rand.nextInt(20); 10 Integer freq = m.get(r); 11 m.put(r, freq == null ? 1 : freq 1); 12 } 13 System.out.println(m); 14 } 15} 16/* 17 * {0=42, 1=44, 2=53, 3=43, 4=44, 5=53, 6=42, 7=53, 8=46, 9=56, 10=58, 11=55, 18 * 12=48, 13=55, 14=52, 15=50, 16=53, 17=50, 18=51, 19=52} 19 */ 20
在main()中,自动包装机制将随机生成的int转换为HashMap可以使用的Integer引用(不能使用基本类型的容器)。如果键不再容器中,get()方法将返回null(这表示该数字第一次被找到)。否则,get()方法将产生与该键相关联的Integer值,然后这个值被递增(自动包装机制再次简化了表达式,但是确实发生了对Integer的包装和拆包)。 下面的示例运行你使用一个String描述来查找Pet,它还展示了你可以使用怎样的方法通过使用containsKey()和containsValue()来测试一个Map,以便查看它是否包含某个键或某个值。 1import java.util.HashMap; 2import java.util.Map; 3import typeinfo.pets.*; 4public class PetMap { 5 public static void main(String[] args) { 6 Map<String,Pet> petMap = new HashMap<String,Pet>(); 7 petMap.put('My Cat',new Cat('Molly')); 8 petMap.put('My Dog', new Dog('Ginger')); 9 petMap.put('My Hamster', new Hamster('Bosco')); 10 System.out.println(petMap); 11 Pet dog = petMap.get('My Dog'); 12 System.out.println(dog); 13 System.out.println(petMap.containsKey('My Dog')); 14 System.out.println(petMap.containsValue(dog)); 15} 16} 17/*{My Dog=Dog Ginger, My Cat=Cat Molly, My Hamster=Hamster Bosco} 18Dog Ginger 19true 20true 21*/
Map与数组和其他的Collection一样,可以很容易地扩展到多维,而我们只需将其值设置为Map(这些Map的值可以是其他容器,甚至是其他Map)。因此,我们能够很容易地将容器组合起来从而快速地生成强大的数据结构。例如,假设你正在跟踪拥有多个宠物的人,你所需只是一个Map<Person,List<Pet>>: 1import java.util.Arrays; 2import java.util.HashMap; 3import java.util.List; 4import java.util.Map; 5import typeinfo.pets.*; 6public class MapOfList { 7 public static Map<Person, List<? extends Pet>> petPeople = new HashMap<Person, List<? extends Pet>>(); 8 static { 9 petPeople.put(new Person('Dawn'), Arrays.asList(new Cymric('Molly'), new Mutt('Spot'))); 10 petPeople.put(new Person('Kate'), 11 Arrays.asList(new Cat('Shackleton'), new Cat('Elsie May'), new Dog('Margrett'))); 12 petPeople.put(new Person('Marilyn'), Arrays.asList(new Pug('Louie aka Louis Snorkelstein Dupree'), 13 new Cat('Stanford aka Stinky el Negro'), new Cat('Pinkola'))); 14 petPeople.put(new Person('Luke'), Arrays.asList(new Rat('Fuzzy'), new Rat('Fizzy'))); 15 petPeople.put(new Person('Isaac'), Arrays.asList(new Rat('Freckly'))); 16 } 17 public static void main(String[] args) { 18 System.out.println('People:' petPeople.keySet()); 19 System.out.println('Pets:' petPeople.values()); 20 for (Person person : petPeople.keySet()) { 21 System.out.println(person ' has:'); 22 for (Pet pet : petPeople.get(person)) { 23 System.out.println(' ' pet); 24 } 25 } 26 } 27} 28/* 29 * People:[Person Dawn, Person Kate, Person Isaac, Person Marilyn, Person Luke] 30 * Pets:[[Cymric Molly, Mutt Spot], [Cat Shackleton, Cat Elsie May, Dog 31 * Margrett], [Rat Freckly], [Pug Louie aka Louis Snorkelstein Dupree, Cat 32 * Stanford aka Stinky el Negro, Cat Pinkola], [Rat Fuzzy, Rat Fizzy]] Person 33 * Dawn has: Cymric Molly Mutt Spot Person Kate has: Cat Shackleton Cat Elsie 34 * May Dog Margrett Person Isaac has: Rat Freckly Person Marilyn has: Pug Louie 35 * aka Louis Snorkelstein Dupree Cat Stanford aka Stinky el Negro Cat Pinkola 36 * Person Luke has: Rat Fuzzy Rat Fizzy 37 */
Map可以返回它的键的Set,它的值的Collection,或者它的键值对的Set。keySet()方法产生了由在petPeople中的所有键组成的Set,它在foreach语句中被用来迭代遍历该Map。
队列是一个典型的先进先出(FIFO)的容器。即从容器的一端放入事物,从另一端取出,并且事物放入容器的顺序与取出的顺序是相同的。队列常被当作一种可靠的将对象从程序的某个区域传输到另一个区域的途径。队列在并发编程中特别重要,因为它们可以安全地将对象从一个任务传输到另一个任务。 LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口,因此LinkedList可以用作 Queue的一种实现。通过将LinkedList向上转型为Queue,下面的示例使用了在Queue接口中与Queue相关的方法: 1import java.util.LinkedList; 2import java.util.Queue; 3import java.util.Random; 4public class QueueDemo { 5 public static void printQ(Queue queue){ 6 while(queue.peek() !=null) 7 System.out.print(queue.remove() ' '); 8 System.out.println(); 9 } 10 public static void main(String[] args) { 11 Queue<Integer> queue = new LinkedList<Integer>(); 12 Random rand = new Random(47); 13 for(int i = 0 ; i <10 ;i ) 14 queue.offer(rand.nextInt(i 10)); 15 System.out.println(queue); 16 Queue<Character> qc = new LinkedList<Character>(); 17 for(char c : 'Brontosaurus'.toCharArray()) 18 qc.offer(c); 19 System.out.println(qc); 20 } 21} 22/*[8, 1, 1, 1, 5, 14, 3, 1, 0, 1] 23[B, r, o, n, t, o, s, a, u, r, u, s] 24*/
offer()方法是与Queue相关的方法之一,它在允许的情况下,将一个元素插入到队尾,或者返回false。peek()和element()都将在不移除的情况下返回队头,但是peek()方法在队列为空时返回null,而element()会抛出NoSuchElementException异常。poll()和remove()方法将移除并返回队头,但是poll()在队列为空时返回null,而remove()会抛出NoSuchElementException异常。 自动包装机制会自动地将nextInt()方法的int结果转换为queue所需的Integer对象,将char c 转换为qc所需的Character对象。Queue接口窄化了对LinkedList的方法的访问权限,以使得只有恰当的方法才可以使用,因此,你能够访问的LinkedList的方法会变少(这里你实际上可以将queue转型回LinkedList,但是至少我们不鼓励这么做)。 注意,与Queue相关的方法提供了完整而独立的功能。即,对于Queue所继承的Collection,在不需要使用它的任何方法的情况下,就可以拥有一个可用的Queue。 先进先出描述了最典型的队列规则。队列规则是指在给定一组队列中的元素的情况下,确定下一个弹出队列的元素的规则。先进先出声明的是下一个元素应该是等待时间最长的元素。 优先级队列声明下一个弹出元素是最需要的元素(具有最高的优先级)。例如,在飞机场,当飞机临近起飞时,这架飞机的乘客可以在办理登记手续时拍到队头。如果构建了一个消息系统,某些消息比其他消息更重要,因而应该更快的得到处理,那么它们何时得到处理就与它们何时到达无关。PriorityQueue添加到Java SE5中,是为了提供这种行为的一种自动实现。 当你在PriorityQueue上调用offer()方法来插入一个对象时,这个对象会在队列中被排序。默认的排序将使用对象在队列中的自然顺序,但是你可以通过提供自己的Comparator来修改这个顺序。PriorityQueue可以确保当你调用peek()、pool()和remove()方法时,获取的元素将是队列中优先级最高的元素。 让PriorityQueue与Integer、String和Character这样的内置类型一起工作易如反掌。在下面的示例中,第一个值集与前一个示例中的随机值相同,因此你可以看到他们从PriorityQueue中弹出的顺序与前一个示例不同: 1import java.util.Arrays; 2import java.util.Collections; 3import java.util.HashSet; 4import java.util.List; 5import java.util.PriorityQueue; 6import java.util.Random; 7import java.util.Set; 8public class PriorityQueueDemo { 9 public static void main(String[] args) { 10 PriorityQueue<Integer> priorityQueue = new PriorityQueue(); 11 Random rand = new Random(47); 12 for (int i = 0; i < 10; i ) 13 priorityQueue.offer(rand.nextInt(i 10)); 14 QueueDemo.printQ(priorityQueue); 15 List<Integer> ints = Arrays.asList(25, 22, 20, 18, 14, 9, 3, 1, 1, 2, 3, 9, 14, 18, 21, 23, 25); 16 priorityQueue = new PriorityQueue<Integer>(ints); 17 QueueDemo.printQ(priorityQueue); 18 priorityQueue = new PriorityQueue<Integer>(ints.size(), Collections.reverseOrder()); 19 priorityQueue.addAll(ints); 20 QueueDemo.printQ(priorityQueue); 21 String fact = 'EDUCATION SHOULD ESCHEW OBFUSCATION'; 22 List<String> strings = Arrays.asList(fact.split('')); 23 PriorityQueue<String> stringPQ = new PriorityQueue<String>(strings); 24 QueueDemo.printQ(stringPQ); 25 stringPQ = new PriorityQueue<String>(strings.size(), Collections.reverseOrder()); 26 stringPQ.addAll(strings); 27 QueueDemo.printQ(stringPQ); 28 Set<Character> charSet = new HashSet<Character>(); 29 for (char c : fact.toCharArray()) 30 charSet.add(c); 31 PriorityQueue<Character> charactersPQ = new PriorityQueue<Character>(charSet); 32 QueueDemo.printQ(charactersPQ); 33 } 34} 35/* 36 * 0 1 1 1 1 1 3 5 8 14 1 1 2 3 3 9 9 14 14 18 18 20 21 22 23 25 25 25 25 23 22 37 * 21 20 18 18 14 14 9 9 3 3 2 1 1 A A B C C C D D E E E F H H I I L N N O O O O 38 * S S S T T U U U W W U U U T T S S S O O O O N N L I I H H F E E E D D C C C B 39 * A A A B C D E F H I L N O S T U W 40 */
你可以看到,重复是允许的,最小的值拥有最高的优先级(如果是String,空格也可以算作值,并且比字母的优先级高)。为了展示你可以使用怎样的方法通过提供自己的Comparator对象来改变顺序,第三个对PriorityQueue<Integer>的构造器调用,和第二个对PriorityQueue<String>的调用使用了由Collection.reverseOrder()(新添加到Java SE5中的)产生的反序的Comparator。 最后一部分添加了一个HashSet来消除重复的Character,这么做只是为了增添点乐趣。 Integer、String和Character可以与PriorityQueue一起工作,因为这些类已经内建了自然排序。如果你想在Priority中使用自己的类,就必须包括额外的功能以产生自然排序,或者必须提供自己的Comparator。
|