首页 > 代码库 > TJI读书笔记15-持有对象

TJI读书笔记15-持有对象

<style> body, td { font-family: 微软雅黑; font-size: 10pt; } </style>

  • TJI读书笔记15-持有对象
    • 总览
    • 类型安全和泛型
    • 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.0
32.class ch11_holdyourObject.Apple
33.1
34.class ch11_holdyourObject.Fushi
35.2
36.class ch11_holdyourObject.Apple
37.*/

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 resizes
22. 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: true
27.
28. pets.remove(h); // Remove by object
29. Pet p = pets.get(2);
30. System.out.println("4: " + p + " " + pets.indexOf(p));
31. //4: Cymric 2
32.
33. Pet cymric = new Cymric();
34. System.out.println("5: " + pets.indexOf(cymric));
35. //5: -1
36.
37. System.out.println("6: " + pets.remove(cymric));
38. //6: false
39.
40. // Must be the exact object:
41. System.out.println("7: " + pets.remove(p));
42. //7: true
43.
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 index
49. 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: true
58.
59. Collections.sort(sub); // In-place sort
60. 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: true
66.
67.
68. Collections.shuffle(sub, rand); // Mix it up
69. System.out.println("shuffled subList: " + sub);
70. //shuffled subList: [Mouse, Manx, Mutt]
71.
72. System.out.println("12: " + pets.containsAll(sub));
73. //12: true
74.
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 copy
85. copy.remove(2); // Remove by index
86. System.out.println("14: " + copy);
87. //14: [Rat, Mouse, Mutt, Pug, Cymric, Pug]
88.
89.
90. copy.removeAll(sub); // Only removes exact objects
91. System.out.println("15: " + copy);
92. //15: [Rat, Mutt, Cymric, Pug]
93.
94. copy.set(1, new Mouse()); // Replace an element
95. System.out.println("16: " + copy);
96. //16: [Rat, Mouse, Cymric, Pug]
97.
98. copy.addAll(2, sub); // Insert a list in the middle
99. System.out.println("17: " + copy);
100. //17: [Rat, Mouse, Mouse, Pug, Cymric, Pug]
101.
102. System.out.println("18: " + pets.isEmpty());
103. //18: false
104.
105. pets.clear(); // Remove all elements
106. System.out.println("19: " + pets);
107. //19: []
108.
109. System.out.println("20: " + pets.isEmpty());
110. //20: true
111.
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: EgyptianMau
119.
120. Pet[] pa = pets.toArray(new Pet[0]);
121. System.out.println("23: " + pa[3].id());
122. //23: 14
123.
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. // 返回一个ListIterator
11. 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():Rat
44.pets.element():Rat
45.pets.peek():Rat
46.pets.remove():Rat
47.pets.removeFirst():Manx
48.pets.poll():Cymric
49.[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():Manx
55.*/

栈是一个后进先出的容器,也就是说最近进去的元素最先出来. 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:true
26.N:false
27.set2 in set1:true
28.set2 in set1:false
29.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.true
22.true
23.*/

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. @Override
13. public Iterator<T> iterator() {
14.
15. return new Iterator<T>() {
16. int current = size() -1;
17. @Override
18. public boolean hasNext() {
19. return current>-1;
20. }
21.
22. @Override
23. 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吧…

TJI读书笔记15-持有对象