body, td {
font-family: 微软雅黑;
font-size: 10pt;
}
- 总览
- 类型安全和泛型
- Collection接口
- 添加元素
- List
- 迭代器
- LinkedList
- 栈
- Set
- Map
- Queue
- Collection和Iterator
- Foreach与迭代器
- 总结
总览
It’s a fairly simple program that only has a fixed quantity of objects with known lifetimes.
埃大爷开篇说了那么一句话.
通常情况下,程序总在运行时才知道根据某些条件创建新的对象,在此之前不知道对象的数量,甚至不知道对象的类型. 这样对程序设计来说就会有个问题,如何在任意时间任意位置去创建任意数量的对象. 当然,数组是一种很好的解决方案,但是数组具有固定的大小,适应问题的灵活度不是很高. 很多时候会显得不够灵活.
java.util类库中提供了一套相当完备的容器类来解决这个问题. 如下图.
java的容器类是用来保存对象的.可以划分为两种概念:
- Collection:是一个元素序列,这些元素序列都服从一定的规则. 比如,list是按照插入顺序保存的元素. set是没有重复的元素集合,queue是按照队列规则保存的元素序列.
- Map:map又叫做关联数组,字典. 是一种”键值对”
类型安全和泛型
java1.5之前,容器类在编译期是没有类型检查一说的. 也就是说,存入容器中的对象都会向上转型成Object类型,再次取出的时候,需要做强转. 那这样就会带来类型安全的问题. 像下面这段代码,如果在add apple的时候混入了orange,那就悲剧了.
1.import java.util.ArrayList;2.3.class Apple{4. private static long counter;5. private final long id = counter++;6. public void id() {7. System.out.println(id); 8. }9.}10.11.class Orange{}12.13.public class ApplesAndOrangesWithoutGenerics {14. @SuppressWarnings("unchecked")15. public static void main(String[] args) {16. ArrayList apples = new ArrayList();17. for(int i = 0;i<3;i++){18. apples.add(new Apple());19. }20.21. for(int i =0;i<apples.size();i++){22. ((Apple)apples.get(i)).id();23. }24. }25.}
在java1.5之后,提供了泛型支持. 在定义容器的时候,可以声明成ArrayList<Apple>
也就是说,在编译过程中会做类型检查. 在从容器中取出时也可以保证是原来的类型. 泛型会在后面的章节详细介绍. 但是这里需要说一句,向上转型也可以作用于泛型.(这是埃大爷的原话,太学术范儿了,说白了就是,这个类型检查只检查是不是属于该类及其子类)
1.import java.util.ArrayList;2.3.4.class Apple{5. private static long counter;6. private final long id = counter++;7. public void id() {8. System.out.println(id); 9. }10.}11.12.class Orange{}13.14.class Fushi extends Apple{}15.16.public class ApplesAndOrangesWithoutGenerics {17.18. public static void main(String[] args) {19.20. ArrayList<Apple> apples = new ArrayList<>();21. apples.add(new Apple());22. apples.add(new Fushi());23. apples.add(new Apple());24.25. for(int i =0;i<apples.size();i++){26. apples.get(i).id();27. System.out.println(apples.get(i).getClass());28. }29. }30.}/*output:31.032.class ch11_holdyourObject.Apple33.134.class ch11_holdyourObject.Fushi35.236.class ch11_holdyourObject.Apple37.*/
Collection接口
Collection概括了一个序列的概念. 是一种存放对象的方式.
1.import java.util.ArrayList;2.import java.util.Collection;3.4.public class SimpleCollection {5. public static void main(String[] args) {6. Collection<Integer> c = new ArrayList<>();7. for(int i =0;i<10;i++){8. c.add(i);9. }10. for(Integer i:c){11. System.out.println(i);12. }13.14. }15.}
很简单的例子,没啥好说的. 但是有一点,这里注意ArrayList的声明方式. 更多的是,我们会采用这么一种方式来声明. 创建一个具体的容器类对象,然后转型成对应的接口.在之后的代码里都使用这个接口. 当然把ArrayList转成Collection有点不合适…
这种做法在后期如果需要做代码优化的时候就会很方便. 比如,发现ArrayList实际表现性能不如LinkedList的时候. 基本只需要修改一行代码就可以了.
添加元素
Collection 添加单个元素很简单,直接add()方法就可以了. 如果想添加一组元素的时候,可以使用Collection自己的addAll方法. 但是更多时候,我们使用的是其工具类Collections.addAll()方法.
1.import java.util.ArrayList;2.import java.util.Arrays;3.import java.util.Collection;4.import java.util.Collections;5.6.public class AddingGroup {7. public static void main(String[] args) {8. Collection<Integer> collection = new ArrayList<>();9.// collection = Arrays.asList(1,2,3,4,5);10.11. Integer[] arrInt = {9,8,7,6}; 12. collection.addAll(Arrays.asList(arrInt));13. Collections.addAll(collection, 11,12,13);14.15. System.out.println(collection);16. }17.}
这里有一个地方需要注意,如果直接将Arrays.asList()的返回值直接传递给list,这个时候虽然表面上看是个list,但是其底层表示的是数组. 因此不能调整尺寸. 这个时候如果使用add或者delete的时候,会报UnSupported Operation的错误.
List
List接口承诺将元素维护在特定的序列之中.而常见的LIst的实现有两种:
- ArrayList:通过名字就可以看出来,ArrayList是一种底层靠数组实现的list,随机访问性能良好,但是插入和移除的时候,开销较大
- LinkedList:通过链表实现,插入和删除时代价较低.但是相比ArrayList随机访问性能较差.
这次真的是一码解千愁了,埃大爷的这逻辑真的不得不让人佩服. 我把结果直接注释到了代码的下面. 我们来看一下:
1.import java.util.ArrayList;2.import java.util.Arrays;3.import java.util.Collections;4.import java.util.List;5.import java.util.Random;6.7.import typeinfo.pets.Cymric;8.import typeinfo.pets.Hamster;9.import typeinfo.pets.Mouse;10.import typeinfo.pets.Pet;11.import typeinfo.pets.Pets;12.13.public class ListFeature {14. public static void main(String[] args) {15. Random rand = new Random(47);16. List<Pet> pets = Pets.arrayList(7);17. System.out.println("1: " + pets);18. //1: [Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug]19.20. Hamster h = new Hamster();21. pets.add(h); // Automatically resizes22. System.out.println("2: " + pets);23. //2: [Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Hamster]24.25. System.out.println("3: " + pets.contains(h));26. //3: true27.28. pets.remove(h); // Remove by object29. Pet p = pets.get(2);30. System.out.println("4: " + p + " " + pets.indexOf(p));31. //4: Cymric 232.33. Pet cymric = new Cymric();34. System.out.println("5: " + pets.indexOf(cymric));35. //5: -136.37. System.out.println("6: " + pets.remove(cymric));38. //6: false39.40. // Must be the exact object:41. System.out.println("7: " + pets.remove(p));42. //7: true43.44. System.out.println("8: " + pets);45. //8: [Rat, Manx, Mutt, Pug, Cymric, Pug]46.47.48. pets.add(3, new Mouse()); // Insert at an index49. System.out.println("9: " + pets);50. //9: [Rat, Manx, Mutt, Mouse, Pug, Cymric, Pug]51.52. List<Pet> sub = pets.subList(1, 4);53. System.out.println("subList: " + sub);54. //subList: [Manx, Mutt, Mouse]55.56. System.out.println("10: " + pets.containsAll(sub));57. //10: true58.59. Collections.sort(sub); // In-place sort60. System.out.println("sorted subList: " + sub);61. //sorted subList: [Manx, Mouse, Mutt]62.63. // Order is not important in containsAll():64. System.out.println("11: " + pets.containsAll(sub));65. //11: true66.67.68. Collections.shuffle(sub, rand); // Mix it up69. System.out.println("shuffled subList: " + sub);70. //shuffled subList: [Mouse, Manx, Mutt]71.72. System.out.println("12: " + pets.containsAll(sub));73. //12: true74.75. List<Pet> copy = new ArrayList<Pet>(pets);76. sub = Arrays.asList(pets.get(1), pets.get(4));77. System.out.println("sub: " + sub);78. //sub: [Mouse, Pug]79.80. copy.retainAll(sub);81. System.out.println("13: " + copy);82. //13: [Mouse, Pug]83.84. copy = new ArrayList<Pet>(pets); // Get a fresh copy85. copy.remove(2); // Remove by index86. System.out.println("14: " + copy);87. //14: [Rat, Mouse, Mutt, Pug, Cymric, Pug]88.89.90. copy.removeAll(sub); // Only removes exact objects91. System.out.println("15: " + copy);92. //15: [Rat, Mutt, Cymric, Pug]93.94. copy.set(1, new Mouse()); // Replace an element95. System.out.println("16: " + copy);96. //16: [Rat, Mouse, Cymric, Pug]97.98. copy.addAll(2, sub); // Insert a list in the middle99. System.out.println("17: " + copy);100. //17: [Rat, Mouse, Mouse, Pug, Cymric, Pug]101.102. System.out.println("18: " + pets.isEmpty());103. //18: false104.105. pets.clear(); // Remove all elements106. System.out.println("19: " + pets);107. //19: []108.109. System.out.println("20: " + pets.isEmpty());110. //20: true111.112. pets.addAll(Pets.arrayList(4));113. System.out.println("21: " + pets);114. //21: [Manx, Cymric, Rat, EgyptianMau]115.116. Object[] o = pets.toArray();117. System.out.println("22: " + o[3]);118. //22: EgyptianMau119.120. Pet[] pa = pets.toArray(new Pet[0]);121. System.out.println("23: " + pa[3].id());122. //23: 14123.124. }125.}
List下常用的方法:
- contains()方法用来确定某个对象是否在列表当中
- remove()方法用于移除某个对象
- indexOf()方法用于发现该对象在List中所处的索引编号.
- sublist()方法,用于截取一个子列表.
- containsAll()方法用于判断,一个list是否位于一个list之中. 这里需要注意,顺序并不影响结果. 换句话说,只要参与比较的list中的元素是被比较的list元素的子集,那就返回true
- retainAll() 求交集
- set() 替换某个index的元素.
- isEmpty()
- clear()
- toArray() 将一个collection转化成数组
这里需要说的一点是,像上面说的这些方法,都涉及到一个查找比较的问题,比如remove()方法,首先得先在list中找到这个这个元素才能操作. 所以这里就需要使用equals()方法. 对于基础类型以及String类型都好说,euqals()方法都已经被重写了. 但是对于自定义的类,就需要以合适的逻辑去重写equals方法,equals方法有时候又会依赖hashcode方法. 所以在重写equals方法的时候,最好需要把hashcode一起给写了.
Collections下常用的方法
- sort()方法,排序. 可以按自然顺序进行排序,也可以根据指定的Comparator进行排序.
- shuffle()方法,以某个随机源进行置换. 说白了就是打乱顺序
注意这俩操作都是所谓的”原地操作”
容器,说到底就是一个维护信息的方式. 而所有信息维护的操作无外乎增删改查. 所以,在使用这些类库的时候,按照这个思路去给众多方法分类是一个不错的办法.
迭代器
使用容器,有个不是很好的地方. 那就是必须要对容器的确切类型进行编程. 迭代器可以消除这一影响. java的Iterator是迭代器的接口. 使用Iterator可以实现:
- 使用iterator()方法可以要求容器返回一个Iterator. Iterator将准备好返回序列的第一个元素.
- 使用next()获得元素中的下一个元素
- 使用hasNext()检查序列中是否还有元素.
1.import java.util.Iterator;2.import java.util.List;3.4.import typeinfo.pets.Pet;5.import typeinfo.pets.Pets;6.7.public class SimpleIterator {8. public static void main(String[] args) {9. List<Pet> pets = Pets.arrayList(5);10. Iterator<Pet> iterator = pets.iterator();11. while(iterator.hasNext()){12. System.out.println(iterator.next());13. }14.15. System.out.println("for each:");16.17. for(Pet eachPet:pets){18. System.out.println(eachPet);19. }20. iterator=pets.iterator();21. iterator.next();22. iterator.remove();23. System.out.println(pets);24. }25.}
Iterator很简单就三个方法:
- hasNext()
- next()
- remove()
需要注意,Iterator只能单向向前移动.下面这个例子就是使用迭代器之后可以做到的,传说中的不依赖容器具体类型的编程方法.
1.import java.util.ArrayList;2.import java.util.HashSet;3.import java.util.Iterator;4.import java.util.LinkedList;5.import java.util.TreeSet;6.7.import typeinfo.pets.Pet;8.import typeinfo.pets.Pets;9.10.public class CrossContainerIteration {11. public static void dispaly(Iterator<Pet> it){12. while(it.hasNext()){13. Pet p = it.next();14. System.out.print(p.id()+":"+p+" ");15. }16. System.out.println();17. }18. public static void main(String[] args) {19. ArrayList<Pet> pets = Pets.arrayList(8);20. LinkedList<Pet> lPets = new LinkedList<>(pets);21. HashSet<Pet> hPets = new HashSet<>(pets);22. TreeSet<Pet> tPets = new TreeSet<>(pets);23.24. dispaly(pets.iterator());25. dispaly(lPets.iterator());26. dispaly(hPets.iterator());27. dispaly(tPets.iterator());28. }29.}
埃大爷说过一句话,This idea of taking a container of objects and passing through it to perform an operation on each one is powerful. 我的理解是,就像display方法一样,第十三行,在需要一个容器中的对象的时候,只需要通过迭代器取出它然后操作就可以了.
ListIterator
ListIterator是针对List做的一个功能加强接口. 它针对各种List做了适配和加强. 比如ListIterator可以双向移动,可以知道当前游标位置前后的元素索引,还可以使用set()方法对当前游标位置的元素进行替换. 另外,除了可以向Iterator那样可以通过list.iterator()返回一个迭代器,还可以制定初始游标位置.
1.import java.util.List;2.import java.util.ListIterator;3.4.import typeinfo.pets.Pet;5.import typeinfo.pets.Pets;6.7.public class ListIteration {8. public static void main(String[] args) {9. List<Pet> pets = Pets.arrayList(8);10. // 返回一个ListIterator11. System.out.println(pets);12. ListIterator<Pet> it = pets.listIterator();13. while (it.hasNext()) {14. System.out.print(it.next() + "," + it.nextIndex() + "," + it.previousIndex() + "; ");15. }16. System.out.println();17.18. //向前移动19. while (it.hasPrevious()) {20. System.out.print(it.previous().id() + " ");21. }22.23. System.out.println();24. System.out.println(pets);25.26. it = pets.listIterator(3);27. while (it.hasNext()) {28. it.next();29. it.set(Pets.randomPet());30. }31. System.out.println(pets);32. }33.}
刚开始我搞错了一点,就是iterator游标的位置,以及nextIndex和previousIndex方法返回的索引值.游标位置应该位于每个元素开始之前,也就是说,如果我使用it = pets.listIterator(1);
的时候,游标应该在绿色的位置而不是红色的位置.(好像是绿色…也可以叫浅蓝色,我不是色盲…)所以next的时候,应该返回索引位置1的元素. 而在这个位置,nextIndex是1,previousIndex是0. 起始位置的previousIndex是-1 而不是最后的8.
LinkedList
其实了解了List和ArrayList之后,LinkedList就很简单的. 至少接口上是差不多的,只是底层实现不同. linkedlist通过链表实现,它可以实现栈,队列和双向队列的方法. (arraylist也可以实现啊, 但是这种只在端点的操作链表好像更擅长).
linkedlist中有一些功能完全一样的方法. 比如getfirst()和element(). 都是返回列表的第一个元素. 在特定的上下文语境中使用特定名称的方法显得更加合适.
简单总结一个
获取第一个元素
- getfirst() 获取但不移除第一个元素,如果list为空,抛NoSuchElementException
- element() 获取但不移除第一个元素,如果list为空,抛NoSuchElementException
- peek() 获取但不移除第一个元素,如果list空位,返回null,不抛异常
- poll() 获取并移除第一个元素,如果列表为空,返回null,不抛异常
- remove() 获取并移除第一个元素,如果列表为空,抛NoSuchElementException
- removeFirst() 获取并移除第一个元素,如果列表为空,抛NoSuchElementException
当然像remove还有其他重载的方法. 需要的时候查api或者IDE环境都有强大的提示功能.
注意add等添加动作默认都是添加到尾巴上的… 这个刚开始想当然了,还以为是插头里…
1.mport java.util.LinkedList;2.3.import typeinfo.pets.Pet;4.import typeinfo.pets.Pets;5.import typeinfo.pets.Rat;6.7.public class LinkedListFeatures {8. public static void main(String[] args) {9. LinkedList<Pet> pets = new LinkedList<>(Pets.arrayList(5));10. System.out.println(pets);11.12. System.out.println("pets.getFirst():"+pets.getFirst());13. System.out.println("pets.element():"+pets.element());14.15. System.out.println("pets.peek():"+pets.peek());16.17.18. System.out.println("pets.remove():"+pets.remove());19. System.out.println("pets.removeFirst():"+pets.removeFirst());20.21.22. System.out.println("pets.poll():"+pets.poll());23. System.out.println(pets);24.25. pets.addFirst(new Rat());26. System.out.println("after addFirst():"+pets);27.28. pets.offer(Pets.randomPet());29. System.out.println("after offer():"+pets);30.31. pets.add(Pets.randomPet());32. System.out.println("after add()"+pets);33.34. pets.addLast(Pets.randomPet());35. System.out.println("after addLast()"+pets);36.37. System.out.println("pets.removeLast():"+pets.removeLast());38.39.40. }41.}/*out:42.[Rat, Manx, Cymric, Mutt, Pug]43.pets.getFirst():Rat44.pets.element():Rat45.pets.peek():Rat46.pets.remove():Rat47.pets.removeFirst():Manx48.pets.poll():Cymric49.[Mutt, Pug]50.after addFirst():[Rat, Mutt, Pug]51.after offer():[Rat, Mutt, Pug, Cymric]52.after add()[Rat, Mutt, Pug, Cymric, Pug]53.after addLast()[Rat, Mutt, Pug, Cymric, Pug, Manx]54.pets.removeLast():Manx55.*/
栈
栈是一个后进先出的容器,也就是说最近进去的元素最先出来. linkedlist可以实现栈的所有功能. 因此可以将linkedlist直接当作栈来使用.
1.public class Stack<T> {2. private LinkedList<T> storage = new LinkedList<>();3. public void push(T v){4. storage.addFirst(v);5. }6. public T peek(){7. return storage.getFirst();8. }9. public T pop(){10. return storage.removeFirst();11. }12. public boolean isEmpty(){13. return storage.isEmpty();14. }15. public String toString(){16. return storage.toString();17. }18.}19.//================================================================20.public class StackTest {21. public static void main(String[] args) {22. Stack<String> stack = new Stack<>();23. for(String s:"I am thecatcher".split(" ")){24. stack.push(s);25. }26. while(!stack.isEmpty()){27. System.out.println(stack.pop()+" ");28. }29. }30.}31.
一个简单的栈的模型,不需要多解释了. 需要说一点,java.utils下提供了一个statck类. 但是据说不太好使.不如自己通过linkedlist实现的stack.
Set
Set也是一个Collection的实现类. Set表示一个没有重复的集合. (是的,就是数学上集合的概念). Set通常被用作来测试归属性,即用来判断某个实例是否是与一个set. 也就是说,Set中最重要的操作是查找. 其中最常用的是HashSet,它对查找做了优化.
Set与Collection具有一毛一样的接口,并没有做扩展. Set提供了三个实现类:
- HashSet:使用hash散列,对查询速度有优化
- TreeSet:使用红黑树结构
- LinkedHashSet:也使用了hash散列,但是使用链表来维护插入顺序.
1.import java.util.HashSet;2.import java.util.Random;3.import java.util.Set;4.5.public class SetOfInteger {6. public static void main(String[] args) {7. Random rand = new Random(47);8. Set<Integer> intSet = new HashSet<>();9. for(int i =0;i<1000000;i++){10. intSet.add(rand.nextInt(30));11. }12. System.out.println(intSet);13. }14.}/*output:15.[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]16.*/
可以看到,给了灰常大的循环去add元素. 但是最终只显示这些.因为set是一个不重复的元素的集合. 按照埃大爷的说法,hashset采用的是散列算法,输出的元素顺序并没有规律可循. 但是我试了好几次,在1.8和1.6下输出的顺序是不大一样的.但是官方文档上,1.8依旧是说不能保证每次结果一致. 不知道是不是一个巧合. mark一下,以后遇到了再看.
Set最常被用于测试归属.
1.import java.util.Collections;2.import java.util.HashSet;3.import java.util.Set;4.5.public class SetOfOperation {6. public static void main(String[] args) {7. Set<String> set1 = new HashSet<>();8. Collections.addAll(set1, "A B C D E F G H I J K L".split(" "));9. set1.add("M");10. System.out.println("H:"+set1.contains("H"));11. System.out.println("N:"+set1.contains("N"));12.13. Set<String> set2 = new HashSet<>();14. Collections.addAll(set2, "H I J K L".split(" "));15. System.out.println("set2 in set1:"+set1.containsAll(set2));16.17. set1.remove("H");18. System.out.println("set2 in set1:"+set1.contains(set2));19. set1.removeAll(set2);20. System.out.println("set1 removeAll set2:"+set1);21. Collections.addAll(set1, "X Y Z".split(" "));22. System.out.println("X Y Z added to set1"+set1);23. }24.}/*output:25.H:true26.N:false27.set2 in set1:true28.set2 in set1:false29.set1 removeAll set2:[A, B, C, D, E, F, G, M]30.X Y Z added to set1[A, B, C, D, E, F, G, M, X, Y, Z]31.*/
Map
Map,字典,关联数组. 都是一个意思,键值对的形式保存对象,将一个对象映射到其他对象上去. 埃大爷貌似有点激动,说这种解决问题的思路很牛.暂时还没感觉,可能还是我太水.
1.import java.util.HashMap;2.import java.util.Map;3.import java.util.Random;4.5.public class Statistics {6. public static void main(String[] args) {7. Random rand = new Random(47);8. Map<Integer, Integer> m = new HashMap<>();9. for (int i = 0; i < 10000; i++) {10. int r = rand.nextInt(20);11. Integer freq = m.get(r);12. m.put(r, freq == null ? 1 : freq + 1);13. }14. System.out.println(m);15. }16.}/*output:17.{0=481, 1=502, 2=489, 3=508, 4=481, 5=503, 6=519, 7=471, 8=468, 9=549, 10=513, 11=531, 12=521, 13=506, 14=477, 15=497, 16=533, 17=509, 18=478, 19=464}18.*/
这是一个简单的例子,将了Map应该怎么用. 不多说了.
1.import java.util.HashMap;2.import java.util.Map;3.4.import typeinfo.pets.Cat;5.import typeinfo.pets.Dog;6.import typeinfo.pets.Pet;7.8.public class PetMap {9. public static void main(String[] args) {10. Map<String, Pet> petMap = new HashMap<>();11. petMap.put("My cat", new Cat("qiuqiu"));12. petMap.put("My dog", new Dog("erqoi"));13. System.out.println(petMap);14.15. Pet dog = petMap.get("My dog");16. System.out.println(petMap.containsKey("My dog"));17. System.out.println(petMap.containsValue(dog));18. }19.}/*output:20.{My dog=Dog erqoi, My cat=Cat qiuqiu}21.true22.true23.*/
Map和数组还有其他的Collection一样,可以很容易的扩展到多维. 比如,Map<Person,List<Pet>>
还有一个小问题,Map如何遍历?通过keyset获取ket的集合,然后遍历这个集合get出value就可以了.
1. for(String key:petMap.keySet()){2. System.out.println(key+"-->"+petMap.get(key));3. }
Queue
表示在之前的学习过程中还真没有接触过java的Queue… 队列,先进先出的容器. 一端进,一端出. LinkedList实现了Queue接口,因此LinkedList可以作为Queue的一种实现.
1.import java.util.LinkedList;2.import java.util.Queue;3.import java.util.Random;4.5.public class QueueDemo {6. public static void printQ(Queue queue){7. while(queue.peek()!=null){8. System.out.print(queue.remove()+" ");9. }10. System.out.println();11. }12. public static void main(String[] args) {13. Queue<Integer> queue = new LinkedList<>();14. Random rand = new Random(47);15. for(int i = 0;i<10;i++){16. queue.offer(rand.nextInt(i+10));17. }18. printQ(queue);19. Queue<Character> qc = new LinkedList<>();20. for(char c:"Thecatcher".toCharArray()){21. qc.offer(c);22. }23. printQ(qc);24. }25.}
PriorityQueue
优先级队列,可以通过Comparator来决定下一个弹出的元素. 优先级队列,在插入时会做排序. 具体实现不想写了.(是的,是就是那么任性…)
Collection和Iterator
Collection是描述所有序列容器的共性的跟接口. 而C++貌似不是这么做的. C++是通过迭代器将所有的序列容器联系到了一起. java的做法看上去更优秀. java既提供了Collection接口,而且还在Collection接口中声明了可以返回迭代器的iterator()方法.
1.public class CollectionAndIterator {2. public static void display(Iterator<Pet> it){3. while(it.hasNext()){4. Pet p=it.next();5. System.out.print(p.id()+" "+p+" ");6. }7. System.out.println();8. }9.10. public static void display(Collection<Pet> pets){11. for(Pet eachPet:pets){12. System.out.print(eachPet.id()+" "+eachPet+" ");13. }14. System.out.println();15. }16.17. public static void main(String[] args) {18. List<Pet> petList = Pets.arrayList(8);19. Set<Pet> petSet = new HashSet<>(petList);20.21. Map<String, Pet> petMap = new LinkedHashMap<>();22. String[] names = "1 2 3 4 5 6 7 8".split(" ");23. for(int i =0;i<names.length;i++){24. petMap.put(names[i], petList.get(i));25. }26. display(petList);27. display(petSet);28.29. display(petList.iterator());30. display(petSet.iterator());31.32. System.out.println(petMap);33. System.out.println(petMap.keySet());34.35. display(petMap.values());36. display(petMap.values().iterator());37.38. }39.}
在Collection的实现类中,其实遍历和iterator效果是一样一样一样的.
但是如果要实现的不是一个Collection的类,那么Iterator使用起来就更加方便了.
Foreach与迭代器
foreach语法可以到可以用于数组,和任何Collection的实现. 但是从更通用的角度说,所有实现Iterable的接口都可以使用foreach.
1.import java.util.Iterator;2.3.public class IterableClass implements Iterable<String>{4. protected String[] words=("I am thecatcher").split(" ");5. public Iterator<String> iterator(){6. return new Iterator<String>() {7. private int index =0;8. public boolean hasNext(){9. return index<words.length;10. }11. public String next(){return words[index++];}12. public void remove(){throw new UnsupportedOperationException();}13. };14. }15. public static void main(String[] args) {16. for(String eachWord:new IterableClass()){17. System.out.print(eachWord+" ");18. }19. }20.21.}
foreach可以应用于所有实现Iterable接口的类. 注意,数组不能自动转化成iterator.需要手动转换一下下.
1.import java.util.Arrays;2.3.public class ArrayIsNotIterable {4. static <T> void test(Iterable<T> ib){5. for(T t:ib){6. System.out.println(t+" ");7. }8. }9.10. public static void main(String[] args) {11. test(Arrays.asList(1,2,3));12. String[] strings = {"A","B","C"};13.// test(strings);14. test(Arrays.asList(strings));15. }16.}
需要注意的是,Iterable接口位于java.lang下,是使用foreach所需要的接口. 只有一个iterator()方法. Iterator接口才是所谓的迭代器. 位于java.utils下. 有next,hasnext,remove三个方法.
一个可以反向遍历的简单实现. 注意这里有两个嵌套的匿名内部类. (搞晕了都…)
1.import java.util.ArrayList;2.import java.util.Arrays;3.import java.util.Collection;4.import java.util.Iterator;5.6.class ReversibleArrayList<T> extends ArrayList<T>{7. public ReversibleArrayList(Collection<T> c){super(c);}8.9. public Iterable<T> reversed(){10. return new Iterable<T>() {11.12. @Override13. public Iterator<T> iterator() {14.15. return new Iterator<T>() {16. int current = size() -1;17. @Override18. public boolean hasNext() {19. return current>-1;20. }21.22. @Override23. public T next() {24. return get(current--);25. }26. public void remove(){27. throw new UnsupportedOperationException();28. }29. };30. }31.32. };33. }34.}35.36.public class AdapterMethodIdiom {37. public static void main(String[] args) {38. ReversibleArrayList<String> ral = new ReversibleArrayList<>(Arrays.asList("to be or not to be".split(" ")));39. for(String s:ral){40. System.out.print(s+" ");41. }42. for(String s:ral.reversed()){43. System.out.print(s+" ");44. }45. }46.}
总结
除了常用方法之外,就是开始的那张图. 困死了,不想多写了. 用到的时候再查API吧…