JAVA容器对象整理
容器对象
1. 容器
1.1 泛型和类型安全
由于ArrayList等容器保存的是object类型.所以通过get()方法拿出来的对象还需要转型到你想要的类型,比如你定义储存Apple类对象的ArrayList容器appleList,你不但可以add Apple类型的对象,也可以add() Orange 类型的对象,因为无论是Apple类还是Orange类在放进容器里的时候已经向上转型为Object类型了.所以当程序运行时通过get方法,把本来是Orange类型强转型为Apple类型的时候就会抛出异常.
j2se5 引进泛型的机制,通过给容器指定类型,比如上述例子,ArrayList appleList<Apple>.这样编译器就会知道这个容器是专门储存Apple类,当add()方法储存Orange类时就会报错.
1.2 基本概念
1.2.1 Collection
独立元素
1.2.2 Map
键值对
1.3 添加一组元素
Collection<Integer> collection = new ArrayList<Integer>(Array.asList(1,2,3,4,5));Integer[] moreInrs = {6,7,8,9,10};collection.addAll(Arrays.asList(moreInts));Collections.addAll(collection,11,12,13,14,15);Collections.addAll(collection,moreInts);1.3.1 Arrays.asList()
接受一个数组或是一个用逗号分隔的元素列表(使用可变参数)
并将其转换为一个List对象
1.3.2 Collections.addAll()
接受一个Collections对象,以及一个数组或是一个用逗号分割的列表.
如:
1.3.3 Collection.addAll()
只能接受另外一个Collection元素作为参数,所以不如Array.asList()和Collections.addAll()灵活.
1.4 容器的打印
import java.util.*;public class PrintingContainers { static Collection fill(Collection<String> collection){ collection.add("rat"); collection.add("cat"); collection.add("dog"); collection.add("dog"); return collection; } static Map fill(Map<String,String> map){ map.put("rat","Fuzzy"); map.put("cat","Rags"); map.put("dog","Bosco"); map.put("dog","spot"); return map; } static void print(String collName,Collection collection){ System.out.println(collName+":"+collection); } static void print(String mapName,Map map){ System.out.println(mapName+":"+map); } public static void main(String[] args){ //List示例 print("ArrayList",fill(new ArrayList<String>())); print("LinkedList",fill(new LinkedList<String>())); //Set示例 print("HashSet",fill(new HashSet<String>())); print("TreeSet",fill(new TreeSet<String>())); print("LinkedHashSet",fill(new LinkedHashSet<String>())); //Map示例 print("HashMap",fill(new HashMap<String,String>())); print("TreeMap",fill(new TreeMap<String,String>())); print("LinkedHashMap",fill(new LinkedHashMap<String,String>())); }}/* Output:ArrayList:[rat, cat, dog, dog]LinkedList:[rat, cat, dog, dog]HashSet:[cat, dog, rat]TreeSet:[cat, dog, rat]LinkedHashSet:[rat, cat, dog]HashMap:{cat=Rags, dog=spot, rat=Fuzzy}TreeMap:{cat=Rags, dog=spot, rat=Fuzzy}LinkedHashMap:{rat=Fuzzy, cat=Rags, dog=spot}*/ArrayList和LinkedList从打印中看出,他们都属于顺序插入,区别在于性能上不同
HashSet,TreeSet,LinkedListSet都属于Set类型,每个相同的项只保存一次,从打印序列看出不同的set实现存储元素的方式不同
HashSet使用比较复杂的方法存储对象
TreeSet按照比较结果的升序存储对象
LinkedListSet按照被添加的顺序存储对象
本例使用3种风格的map,HashMap,TreeMap,LinkedHashMap
HashMap提供了最快的查找速度,但并没有按照顺序保存
TreeMap通过比较结果的升序保存键
LinkedHashMap通过添加顺序保存键,并同时保持了HashMap的速度
1.5 List
1.5.1 ArrayList
长于随机访问元素,但是在List中间插入和移除元素时较慢
(如果你开始在ArrayList中插入大量元素,并且你的元素开始变慢,这时你应该看看你的List实现可能会是罪魁闯祸手)
ps:发现此类瓶颈的最佳方式是仿真器,推介补充材料 http://MindView.net/Books/BetterJava
1.5.2 LinkedList
代价较低的插入和删除操作,优化了顺序访问,但是在随机访问方面比较慢,但是特性集比ArrayList大
1.5.2.1 Stack(以LinkedList实现)
堆栈 后进后出
示例:
import java.util.*;/* * 类名之后的<T>告诉编译器这将是一个参数化类型,是通用范型的应用,在运行时指定. */public class Stack<T> { private LinkedList<T> storage = new LinkedList<T>(); public void push(T v){ storage.addFirst(v);} public T peek(){ return storage.getFirst();} public T pop() {return storage.removeFirst();} public boolean empty(){return storage.isEmpty();} public String toString(){return storage.toString();}}public class StackTest {/** * 在实际应用中看出,Stack<String>指定给泛型T为String类型,同 理也可以指定为其它类型 */public static void main(String[] args) {// TODO Auto-generated method stub Stack<String> stack = new Stack<String>(); for(String s:"My dog has fleas".split(" ")) stack.push(s); while(!stack.empty()) System.out.print(stack.pop()+" ");}}1.5.2.1.1 push()
放入元素到栈中
1.5.2.1.2 peek()
只返回最顶层元素
1.5.2.1.3 pop()
返回最顶层元素并移除最顶层元素
1.5.2.2 Queue(以LinkedList实现)
先进先出
ps:这个规则应该是应用在等待时间最长的元素优先处理的例子
import java.util.LinkedList;import java.util.Queue;import java.util.Random;public class QueueDemo {/** * 这里用java.util库中已经实现好的queue做例子 */public static void printQ(Queue queue){while(queue.peek()!=null) System.out.print(queue.remove()+" "); System.out.println();}public static void main(String[] args) { // TODO Auto-generated method stub Queue<Integer> queue = new LinkedList<Integer>(); Random rand = new Random(47); for(int i =0;i<10;i++) queue.offer(rand.nextInt(i+10)); printQ(queue); Queue<Character> qc =new LinkedList<Character>(); for(char c:"Brontosaurus".toCharArray()) qc.offer(c); printQ(qc); }}1.5.2.2.1 PriorityQueue优先级队列声明下一个弹出元素是最需要的元素(具有最高的优先级)
import java.util.*;public class PriorityQueueDemo {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubPriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>();Random rand = new Random(47);for(int i =0;i<10;i++)priorityQueue.offer(rand.nextInt(i+10));QueueDemo.printQ(priorityQueue);/* * 如果是数字队列的话 priorityQueue会默认按数字从小到大排序,Collections.reverseOrder()为产生反序的Comparator * 通过重载Comparator方法来按照自己的形式作比较 */List<Integer> ints = Arrays.asList(25,22,20,18,14,9,3,1,1,2,3,9,14,18,21,23,25);priorityQueue = new PriorityQueue<Integer>(ints);QueueDemo.printQ(priorityQueue);priorityQueue = new PriorityQueue<Integer>(ints.size(),Collections.reverseOrder());priorityQueue.addAll(ints);QueueDemo.printQ(priorityQueue);/* * 从下面例子看出,空格也包含在字母比较的优先级内 */String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION";List<String> strings = Arrays.asList(fact.split(""));PriorityQueue<String> stringPQ = new PriorityQueue<String>(strings);QueueDemo.printQ(stringPQ);stringPQ = new PriorityQueue<String>(strings.size(),Collections.reverseOrder());stringPQ.addAll(strings);QueueDemo.printQ(stringPQ);//加入了hashset是为了消除重复元素作一个例子Set<Character> charSet = new HashSet<Character>();for(char c:fact.toCharArray())charSet.add(c);PriorityQueue<Character> characterPQ = new PriorityQueue<Character>(charSet);QueueDemo.printQ(characterPQ);}}1.5.3 常用的方法
1.5.3.1 contains()
确定某个对象是否在列表中
1.5.3.2 indexOf()
返回该对象在List中所处的位置
1.5.3.3 equals()
继承自object,根据类的不同比较的方法也有所不同
参见: subList(), retainAll(), removeAll()
1.5.3.3.1 retainAll()
返回两个集合中的有效的交集,这种行为依赖于equals方法
参见: equals()
1.5.3.3.2 removeAll()
删除对象列表含有的所有元素,同样通过equal()判断
参见: equals()
1.5.3.3.3 subList()
允许你从较大的列表中截出一个子列表,而这个子集无论排序怎样改变,父集.containsAll(子集)总是返回true
参见: equals()
1.5.3.4 toArray()
Collection通过toArray转换成一个数组
1.6 迭代器(Iterator)
是一种设计模式,在java里,Iterator是一个对象,只负责遍历并选择容器中的序列,而不用关心该序列底层是用什么实现的。
1.6.1 容器.iterator()
1.6.1.1 ListIterator
Iterator的子类,只能用于List类的访问,可以实现双向移动.
能够使用set方法替换过它最后一个访问的元素.
1.6.2 next()
1.6.3 hasNext()
1.6.4 remove()
1.7 Set
不保存其它元素
1.7.1 HashSet
1.8 HashMap
//以下的例子是随机数的值为键值,随机数出现的次数为值,实现对键值对的包装和拆包
import java.util.*;public class Statistics {public static void main(String[] args) {// TODO Auto-generated method stubRandom rand = new Random(47);Map<Integer,Integer> m = new HashMap<Integer,Integer>();for(int i = 0;i<10000;i++){int r = rand.nextInt(40);Integer freq = m.get(r);m.put(r, freq == null?1:freq+1); //如果键不在map中,则freq返回null,表示第一次被找到,每次roll到相同的值则值递增并更新该键值对的值.}System.out.println(m);}}1.8.1 多维Map
通过容器的组合,可以很容易将map扩展到多维map,比如下面的Map<Person,List<Pet>>
import java.util.*;import cn.edu.zen.eleven.container.*;public class MapOfList {public static Map<Person,List<? extends Pet>> petPeople = new HashMap<Person,List<? extends Pet>>();static{petPeople.put(new Person("Dawn"),Arrays.asList(new Cat(1,"black"),new Dog(2,"B&W")));petPeople.put(new Person("Kate"), Arrays.asList(new Cat(3,"White"),new Cat(4,"Yellow"),new Dog(5,"Brown")));}public static void main(String[] args) {// TODO Auto-generated method stubSystem.out.println("People: "+petPeople.keySet());System.out.println("Pets:"+petPeople.values());/*Map可以返回它的Set,它的值的Collection,或它的键值对Set,keySet()方法产生了由petPeople中的所有键组成的Set.它在ForEach语句中被用来遍历Map*/for(Person person : petPeople.keySet()){System.out.print(person + " has:");for(Pet pet:petPeople.get(person))System.out.print(" "+pet);System.out.println();}}}2. Collection和Iterator
一般来说,针对接口而非具体来编写代码可以应用于更多有相同的特征的对象模型.如编写的方法将接受一个Collection,那么该方法可以应用于任何实现了Collection的类.但是C++类库中并容器的任何公共基类,容器之间的所有特性通过迭代器实现.而在java中,这两种方法绑定在一起,即实现Collection就意味着需要提供iterator()方法
import java.util.*;public class InterfaceVsIterator { //Iterator和Collection都可以将display()方法与底层容器实现解耦public static void display(Iterator<Pet> it){while(it.hasNext()) System.out.print(it.next());System.out.println(); }public static void display(Collection<Pet> pets){for(Pet p:pets)System.out.print(p);System.out.println();}public static void main(String[] args) {// TODO Auto-generated method stubList<Pet> petList = (List<Pet>) Pets.arrayList();Set<Pet> petSet = new HashSet<Pet>(petList);Map<String,Pet> petMap = new LinkedHashMap<String,Pet>();for(Pet pet:petList)petMap.put(pet.petName, pet);display(petList);display(petSet);display(petList.iterator());display(petSet.iterator());System.out.print(petMap);System.out.println();System.out.print(petMap.keySet());System.out.println();display(petMap.values());display(petMap.values().iterator());}}//但是要实现一个不是Collection的外部类时,由于让它去实现Collection接口可能非常困难和麻烦,因此使用iterator会变得非常吸引人.import java.util.*;public class PetSequence { protected List<Pet> pets = Arrays.asList(new Pet(1,"Cat"),new Pet(2,"Dog"),new Pet(3,"Rabbit"),new Pet(4,"Fish"),new Pet(5,"Bird"));}public class NonCollectionSequence extends PetSequence {public Iterator<Pet> iterator(){return new Iterator<Pet>(){private int index = 0;public boolean hasNext(){return index<pets.size();}public Pet next(){return pets.get(index++);}public void remove(){throw new UnsupportedOperationException();}};}public static void main(String[] args){NonCollectionSequence nc =new NonCollectionSequence();Iterator it =nc.iterator();while(it.hasNext())System.out.println(it.next());}}3. Foreach与迭代器
foreach主要用于数组,但在j2se5中引入了新的iterable的接口,并且该接口包含一个能够产生iterator的iterator()方法,并且Iterable能被foreach用来在序列中移动,任何实现了Iterable接口的类,都可以放在foreach语句中.
import java.util.Iterator;public class IterableClass implements Iterable<String> {protected String[] words = ("And that is how"+" we know the Earth to be banana-shaped.").split(" ");public Iterator<String> iterator() {return new Iterator<String>(){private int index = 0;public boolean hasNext(){return index<words.length;}public String next(){ return words[index++];}public void remove(){ throw new UnsupportedOperationException(); }};}public static void main(String[] args){for(String s:new IterableClass())System.out.print(s+" ");}}在下面是显示java所有环境变量的例子,由于Set是一个Iterable(),所以能使用foreach循环
//: holding/EnvironmentVariables.javaimport java.util.*;public class EnvironmentVariables { public static void main(String[] args) { for(Map.Entry entry: System.getenv().entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } }} /* (Execute to see output) *///:~3.1 适配器方法惯用法如果想实现多种Iterable的遍历方式,比如添加一个倒序排序的iterable类,则可以使用设计模式中的迭代器模式.
import java.util.*;public class MultiIterableClass extends IterableClass {public Iterable<String> reversed() { return new Iterable<String>() { public Iterator<String> iterator() { return new Iterator<String>() { int current = words.length - 1; public boolean hasNext() { return current > -1; } public String next() { return words[current--]; } public void remove() { // Not implemented throw new UnsupportedOperationException(); } }; } };}/*注意randomize()并没有返回它自己的Iterator,只返回被打乱顺序的List中的Iterator.Collection.shuffe()方法并没有打乱原来的数组,因为randomized方法将Arrays.asList()的结果包装起来.相当于备份了一个list的副本上打乱顺序*/public Iterable<String> randomized() { return new Iterable<String>() { public Iterator<String> iterator() { List<String> shuffled = new ArrayList<String>(Arrays.asList(words)); Collections.shuffle(shuffled, new Random(47)); return shuffled.iterator(); } };}public static void main(String[] args) { MultiIterableClass mic = new MultiIterableClass(); for(String s : mic.reversed()) System.out.print(s + " "); System.out.println(); for(String s : mic.randomized()) System.out.print(s + " "); System.out.println(); for(String s : mic) System.out.print(s + " ");}}