TJI读书笔记15-持有对象

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

总览

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吧…

时间: 2024-12-09 16:50:18

TJI读书笔记15-持有对象的相关文章

TJI读书笔记12-接口

body, td { font-family: 微软雅黑; font-size: 10pt; } TJI读书笔记12-接口 抽象类和抽象方法 接口 完全解耦和策略模式 接口间的继承关系 工厂模式 乱七八糟不知道怎么归类的知识点 接口和抽象类为我们提供了更强又有力的接口和实现分离的方法. 抽象类和抽象方法 抽象类的目的是为了给子类提供一个通用接口,通过这样一个通用的接口,可以操纵一系列的子类. 也就是说,只需要通过相同的接口调用就可以操作不用的实现. 这也就是所谓的接口和实现的分离. 抽象类简单来

TJI读书笔记16-异常处理

TJI读书笔记16-异常处理 概念 基本异常情形 异常的捕获 自定义异常 异常说明 捕获所有异常 栈轨迹 重新抛出异常 Java标准异常 使用finally 异常的限制 构造器 异常的匹配 其他乱七八糟 概念 在早期没有专门的异常处理机制的时候,比如C语言,会通过一些约定俗成的东西来处理异常. 比如让程序返回某个特殊的值或者设置某个标记. 然后对返回值进行检查以判断程序是否出错. 还记得以前C语言的时候,return 0和return -1对异常处理的实现可以追溯到BASIC中的on error

TJI读书笔记11-多态

body, td { font-family: 微软雅黑; font-size: 10pt; } TJI读书笔记11-多态 再说说向上转型 多态的原理 构造器和多态 协变返回类型 使用继承进行设计 多态是数据抽象和继承之后的第三种基本特征. 一句话说,多态分离了做什么和怎么做(再次对埃大爷佩服的五体投地,简直精辟啊). 是从另外一个角度将接口和实现分离开来. 封装通过合并特征和行为来创建新的数据对象,通过私有化隐藏细节,把接口和实现分离开来. 多态则是消除类型之间的耦合关系. 继承是允许将对象视

TJI读书笔记07-初始化

body, td { font-family: 微软雅黑; font-size: 10pt; } TJI读书笔记07-初始化 成员初始化 构造方法初始化 初始化块 初始化的顺序 成员初始化 java尽量去保证每个变量在使用前都会得到初始化. 对于方法局部变量,java不会自动初始化他们,如果没有显式的初始化,编译器会报错. 对于类的数据成员,java会自动初始化成一个"空""的值.简单来说,这个空的值对于基本数据类型就是,0,false,和空格. 对于引用类型就是null.

TJI读书笔记10-复用类

body, td { font-family: 微软雅黑; font-size: 10pt; } TJI读书笔记10-复用类 组合语法 继承语法 代理 final关键字 final的数据 final的参数 final的方法 final的类 初始化和类的加载 乱七八糟不知道怎么归类的知识点 代码复用是java众多牛逼哄哄的功能之一(好像OOP语言都可以呢-),代码复用的理想状态是,使用类但是又不破坏现有代码. 当然住了复制粘贴以外还有其他的方法. 一个叫组合,一个叫继承. 组合语法 其实刚开始我非

TJI读书笔记09-访问控制权限

body, td { font-family: 微软雅黑; font-size: 10pt; } TJI读书笔记09-访问控制权限 包,package和import 权限修饰符 接口和实现 类的访问权限控制 首先问一个问题,为什么要有访问控制权限? 安全,这当然是一个很重要的原因. 让类库的使用者只能接触他做需要接触的东西. 另外一方面,当我们去重构和修改代码的时候,如何不影响其他的代码和功能?权限访问控制是可以很好的将"变动的事物"和"不变的事物"区分开来.比如一

《Essential C++》读书笔记 之 基于对象编程风格

<Essential C++>读书笔记 之 基于对象编程风格 2014-07-13 4.1 如何实现一个class 4.2 什么是Constructors(构造函数)和Destructors(析构函数) 4.3 何谓mutable(可变)和const(不变) 4.4 什么是this指针 4.5 Static Class Member(静态的类成员) 4.6 打造一个Iterator Class 4.7 合作关系必须建立在友谊的基础上 4.8 实现一个copy assignment operat

Programming in Scala (Second Edition) 读书笔记15 case class and pattern matching

一个算术表达式包含: 数字,变量,二元操作符,一元操作符.用下面几个类来模拟它们 package chapter15 abstract class Expr case class Var(name: String) extends Expr case class Number(num: Double) extends Expr case class UnOp(operator: String, arg: Expr) extends Expr case class BinOp(operator: 

effective java读书笔记——对于所有对象都通用的方法

Java中的所有类都继承自Object类,Object类中有许多通用的方法,这一章要讨论的是:对于Object类中的通用方法,我们的类要不要继承,以及继承时需要注意的事项. 第1条:equals(),覆盖时请遵守通用约定 首先看一下不需要覆盖的情况: 1.类的每个实例本质上是唯一的.(比如Static的,单例的等等),这样不需要特意覆盖equals方法,用Object类的equals()方法就足够了 2.不关心类是否实现了“逻辑相等”的测试功能.我们用equals的目的就是判断两个对象是否是“逻