zoukankan      html  css  js  c++  java
  • Java对象容器(转)

    Java对象容器

     

    范型和类型安全容器

      因为数组是固定大小的,不能符合程序原型时的动态元素的管理,因此Java提供了一系列的容器对象。在读取一般的数组或者集合,例如ArrayList的用法,非常简单,但是在添加元素到集合的时候并没有检查所有的元素类型是否相同,这样做的后果就是在取出元素的时候会出现错误,而且是运行时错误,如下:

    复制代码
    //: holding/ApplesAndOrangesWithoutGenerics.java
    // Simple container example (produces compiler warnings).
    // {ThrowsException}
    import java.util.*;
    class Apple {
      private static long counter;
      private final long id = counter++;
      public long id() { return id; }
    }
    class Orange {}
    public class ApplesAndOrangesWithoutGenerics {
      @SuppressWarnings("unchecked")
      public static void main(String[] args) {
        ArrayList apples = new ArrayList();
        for(int i = 0; i < 3; i++)
          apples.add(new Apple());
          // Not prevented from adding an Orange to apples:
        apples.add(new Orange());
        for(int i = 0; i < apples.size(); i++)
          ((Apple)apples.get(i)).id();
          // Orange is detected only at run time
      }
    } /* (Execute to see output) *///:~
    复制代码

      于是我们可以使用范型来保证类型的安全,使得添加的元素属于同一种数据类型,避免了读取元素时候的元素类型不安全的问题,关于范型的用法如下:

    复制代码
    //: holding/ApplesAndOrangesWithGenerics.java
    import java.util.*;
    public class ApplesAndOrangesWithGenerics {
      public static void main(String[] args) {
        ArrayList<Apple> apples = new ArrayList<Apple>();
        for(int i = 0; i < 3; i++)
          apples.add(new Apple());
        // Compile-time error:
        // apples.add(new Orange());
        for(int i = 0; i < apples.size(); i++)
          System.out.println(apples.get(i).id());
        // Using foreach:
        for(Apple c : apples)
          System.out.println(c.id());
      }
    } /* Output:
    0
    1
    2
    0
    1
    2
    *///:~
    复制代码

    如果上塑类型统一,那么也可以将范型对应类型的子类型上塑添加到范型数组中

    基本概念

      Java的容器类分为两类:Collection,一系列元素,其中List中的元素按照插入的顺序进行排列;Set中不能有重复的元素;Queue按照先进先出的顺序进行数组的添加和读取。Map,一系列的键值集合,ArrayList使用数字下表进行元素读取,因此从一定意义上来说也是一种键值集合;map允许通过一种对象来查找另一种对象。Maps是一个非常有力的编程工具。

      在使用的时候通常是与这些集合的接口的进行交互,而在创建对象的时候才会去使用具体的类型,如List<Apple> apples = new ArrayList<Apple>();

    添加一组元素

       在Arrays和Collections中有添加一组元素的方法,Collection.Arrays.asList()可以将一个数组或者逗号分开的一列元素转换为List对象。Collections.addAll()将一个Collection对象或者一个数组或者一系列用都好分开的元素添加到Collection当中。如下面的例子:

    复制代码
    //: holding/AddingGroups.java
    // Adding groups of elements to Collection objects.
    import java.util.*;
    public class AddingGroups {
      public static void main(String[] args) {
        Collection<Integer> collection =new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5));
        Integer[] moreInts = { 6, 7, 8, 9, 10 };
        collection.addAll(Arrays.asList(moreInts));
        // Runs significantly faster, but you can’t
        // construct a Collection this way:
        Collections.addAll(collection, 11, 12, 13, 14, 15);
        Collections.addAll(collection, moreInts);
        // Produces a list "backed by" an array:
        List<Integer> list = Arrays.asList(16, 17, 18, 19, 20);
        list.set(1, 99); // OK -- modify an element
        // list.add(21); // Runtime error because the
        // underlying array cannot be resized.
      }
    } ///:~
    复制代码

    Collection的构造器可以接收Collection对象来初始化自己,但是Collections.addAll运行速度更快,因此更推荐使用默认构造器创建Collection然后再调用Collections.addAll()。Collection.addAll方法只能接受Collection对象作为参数,但是Array.asList或者Collections.addAll()使用的是参数列表。可以直接使用Arrays.asList,将得到的对象作为List使用,但是底层仍然是作为数组来表示的,当你试图添加或者删除元素的时候,实际上是要试图改变数组的大小,因此会得到一个Unsuported Operation运行时错误。

      Arrays.asList的一个缺陷就是他对传入的类型做最好的猜测,而不管你指定的类型如何,有时候这样会产生错误,如下:

    复制代码
    //: holding/AsListInference.java
    // Arrays.asList() makes its best guess about type.
    import java.util.*;
    class Snow {}
    class Powder extends Snow {}
    class Light extends Powder {}
    class Heavy extends Powder {}
    class Crusty extends Snow {}
    class Slush extends Snow {}
    
    public class AsListInference {
      public static void main(String[] args) {
        List<Snow> snow1 = Arrays.asList(new Crusty(), new Slush(), new Powder());
        //Won’t compile:
        //List<Snow> snow2 = Arrays.asList(new Light(), new Heavy());
        //Compiler says:
        //found: java.util.List<Powder>
        //required: java.util.List<Snow>
        // Collections.addAll() doesn’t get confused:
        List<Snow> snow3 = new ArrayList<Snow>();
        Collections.addAll(snow3, new Light(), new Heavy());
        // Give a hint using an explicit type argument specification:
        List<Snow> snow4 = Arrays.<Snow>asList(new Light(), new Heavy());
      }
    } ///:~
    复制代码

    容器打印

    复制代码
    //: holding/PrintingContainers.java
    // Containers print themselves automatically.
    import java.util.*;
    import static net.mindview.util.Print.*;
    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;
      }
      public static void main(String[] args) {
        print(fill(new ArrayList<String>()));
        print(fill(new LinkedList<String>()));
        print(fill(new HashSet<String>()));
        print(fill(new TreeSet<String>()));
        print(fill(new LinkedHashSet<String>()));
        print(fill(new HashMap<String,String>()));
        print(fill(new TreeMap<String,String>()));
        print(fill(new LinkedHashMap<String,String>()));
      }
    } /* Output:
    [rat, cat, dog, dog]
    [rat, cat, dog, dog]
    [dog, cat, rat]
    Holding Your Objects
    281 
    [cat, dog, rat]
    [rat, cat, dog]
    {dog=Spot, cat=Rags, rat=Fuzzy}
    {cat=Rags, dog=Spot, rat=Fuzzy}
    {rat=Fuzzy, cat=Rags, dog=Spot}
    *///:~
    复制代码

    List

      List按照添加的顺序存放元素,List接口比Collection接口增加了允许插入和删除的方法,List有两种,ArrayList随机获取数据较快,但是在插入和一出元素的时候较慢;LinkedList用来顺序获取元素,插入和移除元素较快,但是随机获取元素较慢,但是比ArrayList有更多的特性:  

    复制代码
    //: holding/ListFeatures.java
    import typeinfo.pets.*;
    import java.util.*;
    import static net.mindview.util.Print.*;
    public class ListFeatures {
      public static void main(String[] args) {
        Random rand = new Random(47);
        List<Pet> pets = Pets.arrayList(7);
        print("1: " + pets);
        Hamster h = new Hamster();
        pets.add(h); // Automatically resizes
        print("2: " + pets);
        print("3: " + pets.contains(h));
        pets.remove(h); // Remove by object
        Pet p = pets.get(2);
        print("4: " + p + " " + pets.indexOf(p));
        Pet cymric = new Cymric();
        print("5: " + pets.indexOf(cymric));
        print("6: " + pets.remove(cymric));
        // Must be the exact object:
        print("7: " + pets.remove(p));
        print("8: " + pets);
        pets.add(3, new Mouse()); // Insert at an index 
        print("9: " + pets);
        List<Pet> sub = pets.subList(1, 4);
        print("subList: " + sub);
        print("10: " + pets.containsAll(sub));
        Collections.sort(sub); // In-place sort
        print("sorted subList: " + sub);
        // Order is not important in containsAll():
        print("11: " + pets.containsAll(sub));
        Collections.shuffle(sub, rand); // Mix it up
        print("shuffled subList: " + sub);
        print("12: " + pets.containsAll(sub));
        List<Pet> copy = new ArrayList<Pet>(pets);
        sub = Arrays.asList(pets.get(1), pets.get(4));
        print("sub: " + sub);
        copy.retainAll(sub);
        print("13: " + copy);
        copy = new ArrayList<Pet>(pets); // Get a fresh copy
        copy.remove(2); // Remove by index
        print("14: " + copy);
        copy.removeAll(sub); // Only removes exact objects
        print("15: " + copy);
        copy.set(1, new Mouse()); // Replace an element
        print("16: " + copy);
        copy.addAll(2, sub); // Insert a list in the middle
        print("17: " + copy);
        print("18: " + pets.isEmpty());
        pets.clear(); // Remove all elements
        print("19: " + pets);
        print("20: " + pets.isEmpty());
        pets.addAll(Pets.arrayList(4));
        print("21: " + pets);
        Object[] o = pets.toArray();
        print("22: " + o[3]);
        Pet[] pa = pets.toArray(new Pet[0]);
        print("23: " + pa[3].id());
      }
    } /* Output:
    1: [Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug]
    2: [Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Hamster]
    3: true
    4: Cymric 2
    5: -1
    6: false
    7: true
    8: [Rat, Manx, Mutt, Pug, Cymric, Pug]
    9: [Rat, Manx, Mutt, Mouse, Pug, Cymric, Pug]
    subList: [Manx, Mutt, Mouse]
    10: true
    sorted subList: [Manx, Mouse, Mutt]
    11: true
    shuffled subList: [Mouse, Manx, Mutt]
    12: true
    sub: [Mouse, Pug]
    13: [Mouse, Pug]
    14: [Rat, Mouse, Mutt, Pug, Cymric, Pug]
    15: [Rat, Mutt, Cymric, Pug]
    16: [Rat, Mouse, Cymric, Pug]
    17: [Rat, Mouse, Mouse, Pug, Cymric, Pug]
    18: false
    19: []
    20: true
    21: [Manx, Cymric, Rat, EgyptianMau]
    22: EgyptianMau
    284
    Thinking in Java
    Bruce Eckel
    23: 14
    *///:~
    复制代码

    历遍器

       有些时候我们不知道我们将要使用的容器类型,或者说我们要对多种容器进行相同的历遍操作,这个时候如何做到代码与容器类型的分离,以便实现代码的重用呢?Java中提供了历遍器可以有效地将操作与容器类型相分离,如下:

    复制代码
    //: holding/CrossContainerIteration.java
    import typeinfo.pets.*;
    import java.util.*;
    public class CrossContainerIteration {
      public static void display(Iterator<Pet> it) {
        while(it.hasNext()) {
          Pet p = it.next();
          System.out.print(p.id() + ":" + p + " ");
        }
        System.out.println();
      }
      public static void main(String[] args) {
        ArrayList<Pet> pets = Pets.arrayList(8);
        LinkedList<Pet> petsLL = new LinkedList<Pet>(pets);
        HashSet<Pet> petsHS = new HashSet<Pet>(pets);
        TreeSet<Pet> petsTS = new TreeSet<Pet>(pets);
        display(pets.iterator());
        display(petsLL.iterator());
        display(petsHS.iterator());
        display(petsTS.iterator());
      }
    } /* Output:
    0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
    0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
    4:Pug 6:Pug 3:Mutt 1:Manx 5:Cymric 7:Manx 2:Cymric 0:Rat
    5:Cymric 2:Cymric 7:Manx 1:Manx 3:Mutt 6:Pug 4:Pug 0:Rat
    *///:~
    复制代码

      Iterator只能实现的功能有准备读取序列中的第一个元素;获取下一个元素;查看是否还有下一个元素;移除刚刚获取到的元素。ListIterator比Iterator功能更加强大,但是只对List有作用,Iterator只能前向移动,但是ListIterator是双向的,可以获取当前元素的前一个和下一个元素,可以设置当前元素的值,可以使用ListIterator来准备读取第一个元素,也可以通过传递索引来从指定的索引处开始读取,如下:

    复制代码
    //: holding/ListIteration.java
    import typeinfo.pets.*;
    import java.util.*;
    public class ListIteration {
      public static void main(String[] args) {
        List<Pet> pets = Pets.arrayList(8);
        ListIterator<Pet> it = pets.listIterator();
        while(it.hasNext())
          System.out.print(it.next() + ", " + it.nextIndex() + ", " + it.previousIndex() + "; ");
        System.out.println();
        // Backwards:
        while(it.hasPrevious())
          System.out.print(it.previous().id() + " ");
        System.out.println();
        System.out.println(pets);
        it = pets.listIterator(3);
        while(it.hasNext()) {
          it.next();
          it.set(Pets.randomPet());
        }
        System.out.println(pets);
      }
    } /* Output:
    Rat, 1, 0; Manx, 2,
    5; Pug, 7, 6; Manx,
    7 6 5 4 3 2 1 0
    [Rat, Manx, Cymric,
    [Rat, Manx, Cymric,
    *///:
    复制代码

    LinkedList

       LinkedList继承自List接口,但是在插入和移除方面更加有效率,但是在随机读取方面效率较低,LinkedList添加了一些方法使得自身的性能更像是队列或者双向队列:

    复制代码
    //: holding/LinkedListFeatures.java
    import typeinfo.pets.*;
    import java.util.*;
    import static net.mindview.util.Print.*;
    public class LinkedListFeatures {
      public static void main(String[] args) {
        LinkedList<Pet> pets = new LinkedList<Pet>(Pets.arrayList(5));
        print(pets);
        // Identical:
        print("pets.getFirst(): " + pets.getFirst());
        print("pets.element(): " + pets.element());
        // Only differs in empty-list behavior:
        print("pets.peek(): " + pets.peek());
        // Identical; remove and return the first element:
        print("pets.remove(): " + pets.remove());
        print("pets.removeFirst(): " + pets.removeFirst());
        // Only differs in empty-list behavior:
        print("pets.poll(): " + pets.poll());
        print(pets);
        pets.addFirst(new Rat());
        print("After addFirst(): " + pets);
        pets.offer(Pets.randomPet());
        print("After offer(): " + pets);
        pets.add(Pets.randomPet());
        print("After add(): " + pets);
        pets.addLast(new Hamster());
        print("After addLast(): " + pets);
        print("pets.removeLast(): " + pets.removeLast());
      }
    } /* Output:
    [Rat, Manx, Cymric, Mutt, Pug]
    pets.getFirst(): Rat
    pets.element(): Rat
    pets.peek(): Rat
    pets.remove(): Rat
    pets.removeFirst(): Manx
    pets.poll(): Cymric
    [Mutt, Pug]
    After addFirst(): [Rat, Mutt, Pug]
    After offer(): [Rat, Mutt, Pug, Cymric]
    After add(): [Rat, Mutt, Pug, Cymric, Pug]
    After addLast(): [Rat, Mutt, Pug, Cymric, Pug, Hamster]
    pets.removeLast(): Hamster
    *///:~
    复制代码

    Stack

      栈是一种先进后出的数据结构,LinkedList有一些方法可以直接应用于栈,因此可以使用LinkedList而不是使用栈,但是有些时候栈可以更好的表达我们想要的东西:

    复制代码
    //: net/mindview/util/Stack.java
    // Making a stack from a LinkedList.
    package net.mindview.util;
    import java.util.LinkedList;
    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(); }
    } ///:~
    复制代码

    如果想要获得栈的性质,这里使用继承是不合适的。因为这样派生出来的类不仅仅具有栈的功能,还具备了LinkedList的其他功能,下面就是这个我们自己的栈和java.util中的Stack的用法:

    复制代码
    //: holding/StackCollision.java
    import net.mindview.util.*;
    public class StackCollision {
      public static void main(String[] args) {
        net.mindview.util.Stack<String> stack = new net.mindview.util.Stack<String>();
        for(String s : "My dog has fleas".split(" "))
          stack.push(s);
        while(!stack.empty())
          System.out.print(stack.pop() + " ");
        System.out.println();
        java.util.Stack<String> stack2 = new java.util.Stack<String>();
        for(String s : "My dog has fleas".split(" "))
          stack2.push(s);
        while(!stack2.empty())
        System.out.print(stack2.pop() + " ");
      }
    } /* Output:
    fleas has dog My
    fleas has dog My
    *///:~
    复制代码

    虽然java.util.Stack存在,但是用LinkedList创建的Stack性能更好,因此更推荐使用我们自己创建的栈结构。

    Set

      Set只能保存不想同的元素,如果存储相同的元素多次,将会报错。Set最长用的场景是测试元素之间的关系,例如你可以询问某个对象是否在Set中。因为这样,Set中最常用也是最重要的功能是查找,同长我们选择HashSet来进行这个工作。Set继承了Collection接口,而且除了这个接口没有实现任何其他的功能。

      HashSet采用哈系算法,没有对元素进行排序,TreeSet采用了红黑树数据结构,因此HashSet得到的结果是无序的,TreeSet得到的结果是有序的。

      Set中最长用的功能就是contains来测试集合之间的关系,如下:

    复制代码
    //: holding/SetOperations.java
    import java.util.*;
    import static net.mindview.util.Print.*;
    public class SetOperations {
      public static void main(String[] args) {
        Set<String> set1 = new HashSet<String>();
        Collections.addAll(set1,"A B C D E F G H I J K L".split(" "));
        set1.add("M");
        print("H: " + set1.contains("H"));
        print("N: " + set1.contains("N"));
        Set<String> set2 = new HashSet<String>();
        Collections.addAll(set2, "H I J K L".split(" "));
        print("set2 in set1: " + set1.containsAll(set2));
        set1.remove("H");
        print("set1: " + set1);
        print("set2 in set1: " + set1.containsAll(set2));
        set1.removeAll(set2);
        print("set2 removed from set1: " + set1);
        Collections.addAll(set1, "X Y Z".split(" "));
        print("‘X Y Z’ added to set1: " + set1);
      }
    } /* Output:
    H: true
    N: false
    set2 in set1: true
    set1: [D, K, C, B, L, G, I, M, A, F, J, E]
    set2 in set1: false
    set2 removed from set1: [D, C, B, G, M, A, F, E]
    ‘X Y Z’ added to set1: [Z, D, C, B, G, M, A, F, Y, X, E]
    *///:~
    复制代码

      Set中不能存储重复对象的特性非常有用,例如列举出出现的所有单词,还可以使用TreeSet将结果按照顺序输出:

    复制代码
    //: holding/UniqueWordsAlphabetic.java
    // Producing an alphabetic listing.
    import java.util.*;
    import net.mindview.util.*;
    public class UniqueWordsAlphabetic {
      public static void main(String[] args) {
        Set<String> words = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
        words.addAll(new TextFile("SetOperations.java", "\\W+"));
        System.out.println(words);
      }
    } /* Output:
    [A, add, addAll, added, args, B, C, class, Collections, contains,
    containsAll, D, E, F, false, from, G, H, HashSet, holding, I, import,
    in, J, java, K, L, M, main, mindview, N, net, new, Output, Print,
    public, remove, removeAll, removed, Set, set1, set2, SetOperations,
    split, static, String, to, true, util, void, X, Y, Z]
    *///:~
    复制代码

    Map

       在编程中通过一种对象索引另外一个对象非常有用。例如下面的代码用来检测java生成的随机数是否平均分布:

    复制代码
    //: holding/Statistics.java
    // Simple demonstration of HashMap.
    import java.util.*;
    public class Statistics {
      public static void main(String[] args) {
        Random rand = new Random(47);
        Map<Integer,Integer> m = new HashMap<Integer,Integer>();
        for(int i = 0; i < 10000; i++) {
          // Produce a number between 0 and 20:
          int r = rand.nextInt(20);
          Integer freq = m.get(r);
          m.put(r, freq == null ? 1 : freq + 1);
        }
        System.out.println(m);
      }
    } /* Output
    复制代码

      另外Map还可以嵌套使用,只需要将其中的值设置为Map结构就可以,如下:

    复制代码
    //: holding/MapOfList.java
    package holding;
    import typeinfo.pets.*;
    import java.util.*;
    import static net.mindview.util.Print.*;
    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 Cymric("Molly"),new Mutt("Spot")));
        petPeople.put(new Person("Kate"),
        Arrays.asList(new Cat("Shackleton"),
        new Cat("Elsie May"), new Dog("Margrett")));
        petPeople.put(new Person("Marilyn"),
        Arrays.asList(
          new Pug("Louie aka Louis Snorkelstein Dupree"),
          new Cat("Stanford aka Stinky el Negro"),
          new Cat("Pinkola")));
        petPeople.put(new Person("Luke"),
        Arrays.asList(new Rat("Fuzzy"), new Rat("Fizzy")));
        petPeople.put(new Person("Isaac"),
        Arrays.asList(new Rat("Freckly")));
      }
      public static void main(String[] args) {
        print("People: " + petPeople.keySet());
        print("Pets: " + petPeople.values());
        for(Person person : petPeople.keySet()) {
          print(person + " has:");
          for(Pet pet : petPeople.get(person))
          print(" " + pet);
        }
      }
    } /* Output:
    复制代码

    Queue

      队列是一种先进先出的数据结构,出队的顺序和入队的顺序相同。队列在对对象从一处转移到另外一处的时候非常有用,尤其是在并发编程的时候。LinkedList有支持队列特性的方法,同时还继承了Queue接口,因此一个LinkedList可以当作Queue来使用,通过将LinkedList上塑造型到Queue,可以使用队列的一系列方法:

    复制代码
    //: holding/QueueDemo.java
    // Upcasting to a Queue from a LinkedList.
    import java.util.*;
    public class QueueDemo {
      public static void printQ(Queue queue) {
        while(queue.peek() != null)
          System.out.print(queue.remove() + " ");
        System.out.println();
      }
      public static void main(String[] args) {
        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);
      }
    } /* Output:
    8 1 1 1 5 14 3 1 0 1
    B r o n t o s a u r u s
    *///:~
    复制代码

      上面介绍的是一般队列,按照等待的时间进行先进先出,但是有些事后我们需要根据对象被需要的等级来安排出队的顺序,这个时候我们需要使用优先级队列,PriorityQueue。当使用offer添加对象到PriorityQueue的时候,对象在队列中被排序,默认的排序方法是使用对象的自然顺序,但是可以通过提供自己的Comparator来改变对象的排列顺序,你首先得到的对象是最高优先等级的对象:

    复制代码
    //: holding/PriorityQueueDemo.java
    import java.util.*;
    public class PriorityQueueDemo {
      public static void main(String[] args) {
        PriorityQueue<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);
        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);
        Set<Character> charSet = new HashSet<Character>();
        for(char c : fact.toCharArray())
          charSet.add(c); // Autoboxing
        PriorityQueue<Character> characterPQ = new PriorityQueue<Character>(charSet);
        QueueDemo.printQ(characterPQ);
      }
    } /* Output:
    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 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 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 A A
    A B C D E F H I L N O S T U W
    *///:~
    复制代码

      Integer,String和Character都可以与PriorityQueue配合,因为他们已经有内置的排序方法,如果需要使用自己创建的类与PriorityQueue使用,你必须包含附加的排序方法,或者Comparator。

    集合vs历遍器

      Collection提供了所有有序序列的容器接口,另外,java.utilAbstractCollection类提供了Collection的实现,你可以通过创建AbstractCollection的子类而不用书写重复的代码。使用Collection接口而不使用具体类型的好处就是代码重用,任何继承自Collection接口的类型都可以在这个方法中使用。另外,Java中Collection和iterator是绑定到一起的,继承了Collection就意味着提供了iterator方法:

    复制代码
    //: holding/InterfaceVsIterator.java
    import typeinfo.pets.*;
    import java.util.*;
    public class InterfaceVsIterator {
      public static void display(Iterator<Pet> it) {
        while(it.hasNext()) {
          Pet p = it.next();
          System.out.print(p.id() + ":" + p + " ");
        }
        System.out.println();
      }
      public static void display(Collection<Pet> pets) {
        for(Pet p : pets)
          System.out.print(p.id() + ":" + p + " ");
        System.out.println();
      }
      public static void main(String[] args) {
        List<Pet> petList = Pets.arrayList(8);
        Set<Pet> petSet = new HashSet<Pet>(petList);
        Map<String,Pet> petMap = new LinkedHashMap<String,Pet>(); 
        String[] names = ("Ralph, Eric, Robin, Lacey, " + "Britney, Sam, Spot, Fluffy").split(", ");
        for(int i = 0; i < names.length; i++)
          petMap.put(names[i], petList.get(i));
        display(petList);
        display(petSet);
        display(petList.iterator());
        display(petSet.iterator());
        System.out.println(petMap);
        System.out.println(petMap.keySet());
        display(petMap.values());
        display(petMap.values().iterator());
      }
    } /* Output:
    0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
    4:Pug 6:Pug 3:Mutt 1:Manx 5:Cymric 7:Manx 2:Cymric 0:Rat
    0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
    4:Pug 6:Pug 3:Mutt 1:Manx 5:Cymric 7:Manx 2:Cymric 0:Rat
    {Ralph=Rat, Eric=Manx, Robin=Cymric, Lacey=Mutt, Britney=Pug,
    Sam=Cymric, Spot=Pug, Fluffy=Manx}
    [Ralph, Eric, Robin, Lacey, Britney, Sam, Spot, Fluffy]
    0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
    0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
    *///:~
    复制代码

      Iterator的使用在涉及到不能上塑造型到Collection的时候就显得尤为重要。例如我们通过继承一个承载了Pet对象的类来创建一个Collection的实现,我们必须实现Collection的所有方法,即使我们在display方法中不许要使用他们。虽然这可以通过继承自AbstractCollection来实现,你还是必须实现iterator()方法和size()来弥补AbstractCollection没有实现的方法,而且这些方法有可能在AbstractCollection的其他方法中使用到:

    复制代码
    //: holding/CollectionSequence.java
    import typeinfo.pets.*;
    import java.util.*;
    public class CollectionSequence
    extends AbstractCollection<Pet> {
      private Pet[] pets = Pets.createArray(8);
      public int size() { return pets.length; }
      public Iterator<Pet> iterator() {
        return new Iterator<Pet>() {
          private int index = 0;
          public boolean hasNext() {
            return index < pets.length;
          }
          public Pet next() { return pets[index++]; }
          public void remove() { // Not implemented
            throw new UnsupportedOperationException();
          }
        };
      }
      public static void main(String[] args) {
        CollectionSequence c = new CollectionSequence();
        InterfaceVsIterator.display(c);
        InterfaceVsIterator.display(c.iterator());
      }
    } /* Output:
    0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
    0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
    *///:~
    复制代码

      从上面的例子中我们可以看到,如果需要继承Collection,你必须实现iterator方法,但是如果你的类已经继承了其他的类,那么你就不能继承自AbstractCollection,这个时候,继承自Collection你就必须实现接口中的所有方法,因此通过继承和添加iterator就显得方便许多:

    复制代码
    //: holding/NonCollectionSequence.java
    import typeinfo.pets.*;
    import java.util.*;
    class PetSequence {
      protected Pet[] pets = Pets.createArray(8);
    }
    public class NonCollectionSequence extends PetSequence {
      public Iterator<Pet> iterator() {
        return new Iterator<Pet>() {
          private int index = 0;
          public boolean hasNext() {
            return index < pets.length;
          }
          public Pet next() { return pets[index++]; }
          public void remove() { // Not implemented
            throw new UnsupportedOperationException();
          }
        };
      }
      public static void main(String[] args) {
        NonCollectionSequence nc = new NonCollectionSequence();
        InterfaceVsIterator.display(nc.iterator());
      }
    } /* Output:
    0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
    *///:~
    复制代码

      通过提供Iterator是最松耦合的将序列与历遍序列相联系的方法,而且要比继承Collection要少得许多的限制。

    Foreach和历遍器

      freach语法可以对所有Collection对象使用。之所以可以的原因是Java SE5中引入了新的接口Iterable,包含了iterator方法用来生成iterator,foreach语法就是通过Iterable接口来实现历遍的,任何继承了Iterable接口的对象都可以使用foreach语法:

    复制代码
    //: holding/IterableClass.java
    // Anything Iterable works with foreach.
    import java.util.*;
    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() { // Not implemented
            throw new UnsupportedOperationException();
          }
        };
      }
      public static void main(String[] args) {
        for(String s : new IterableClass())
          System.out.print(s + " ");
      }
    } /* Output:
    And that is how we know the Earth to be banana-shaped.
    *///:~
    复制代码

      foreach语句可以用于数组或者任何Iterable接口,但是这并不意味着一个数组可以自动地转换成为Iterable,有没有任何装箱操作发生:

    复制代码
    //: holding/ArrayIsNotIterable.java
    import java.util.*;
    public class ArrayIsNotIterable {
      static <T> void test(Iterable<T> ib) {
        for(T t : ib)
          System.out.print(t + " ");
      }
      public static void main(String[] args) {
        test(Arrays.asList(1, 2, 3));
        String[] strings = { "A", "B", "C" };
        // An array works in foreach, but it’s not Iterable:
        //! test(strings);
        // You must explicitly convert it to an Iterable:
        test(Arrays.asList(strings));
      }
    } /* Output:
    1 2 3 A B C
    *///:~
    复制代码

      如果是已经有了一个继承自Iterable的类,但是因为值提供了一种历遍方法,现在我希望有不同的历遍方法,例如前向和反向历遍,以便我可以选择使用那种历遍方法,该如何解决呢?一种方案就是使用适配器模式,当需要一种接口来适合另外一个接口的时候可以使用这种模式。下面添加一种逆向的历遍方法,不能重写,只能添加一个方法来产生一个Iterable对象,然后使用这个对象来完成foreach语句,这样就可以完成多种历遍方式:

    复制代码
    //: holding/AdapterMethodIdiom.java
    // The "Adapter Method" idiom allows you to use foreach
    // with additional kinds of Iterables.
    import java.util.*;
    class ReversibleArrayList<T> extends ArrayList<T> {
      public ReversibleArrayList(Collection<T> c) { super(c); }
      public Iterable<T> reversed() {
        return new Iterable<T>() {
          public Iterator<T> iterator() {
            return new Iterator<T>() {
              int current = size() - 1;
              public boolean hasNext() { return current > -1; }
              public T next() { return get(current--); }
              public void remove() { // Not implemented
                throw new UnsupportedOperationException();
              }
            };
          }
        };
      }
    }
    public class AdapterMethodIdiom {
      public static void main(String[] args) {
        ReversibleArrayList<String> ral = new ReversibleArrayList<String>(Arrays.asList("To be or not to be".split(" ")));
        // Grabs the ordinary iterator via iterator():
        for(String s : ral)
          System.out.print(s + " ");
        System.out.println();
        // Hand it the Iterable of your choice
        for(String s : ral.reversed())
          System.out.print(s + " ");
      }
    } /* Output:
    To be or not to be
    be to not or be To
    *///:~
    复制代码

    使用这种方法还可以添加另外两种历遍的方法:

    复制代码
    //: holding/MultiIterableClass.java
    // Adding several Adapter Methods.
    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();
              }
            };
          }
        };
      }
      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 + " ");
      }
    } /* Output:
    banana-shaped. be to Earth the know we how is that And
    is banana-shaped. Earth that how the be And we know to
    And that is how we know the Earth to be banana-shaped.
    *///:~
    复制代码

      我们可以看到random方法没有创建自己的Iterator仅仅是返回了乱序的List。我们可以看到Collections.shuffled方法不影响原数组的顺序,仅仅是打乱shuffle中的引用而已。之所以这样是因为randomized方法中先将Array.asList封装到ArrayList中,如果直接对Array.asList洗牌,那么产生List将会改变原数组中的是顺序,如下:

    复制代码
    //: holding/ModifyingArraysAsList.java
    import java.util.*;
    public class ModifyingArraysAsList {
      public static void main(String[] args) {
        Random rand = new Random(47);
        Integer[] ia = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));
        System.out.println("Before shuffling: " + list1);
        Collections.shuffle(list1, rand);
        System.out.println("After shuffling: " + list1);
        System.out.println("array: " + Arrays.toString(ia));
        List<Integer> list2 = Arrays.asList(ia);
        System.out.println("Before shuffling: " + list2);
        Collections.shuffle(list2, rand);
        System.out.println("After shuffling: " + list2);
        System.out.println("array: " + Arrays.toString(ia));
      }
    } /* Output:
    Before shuffling: [1, 2, 3, 4, 5, 6, 7, 8, 9,
    After shuffling: [4, 6, 3, 1, 8, 7, 2, 5, 10,
    array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    Before shuffling: [1, 2, 3, 4, 5, 6, 7, 8, 9,
    After shuffling: [9, 1, 6, 3, 7, 2, 5, 10, 4,
    array: [9, 1, 6, 3, 7, 2, 5, 10, 4, 8]
    *///:~
    复制代码
  • 相关阅读:
    iOS实现微信外部H5支付完成后返回原APP(多APP也可实现)
    微信开发,仿原生实现 “再按一次返回”功能
    centos 环境变量配置
    centos 6.* 配置端口
    linux 中 svn 服务器搭建 重启
    删除apache注册表
    Linux 编译安装 php 扩展包 curl
    手机APP中使用history.back()返回没有效果的解决
    Centos 6.5升级安装Git
    linux 已有目录挂载磁盘
  • 原文地址:https://www.cnblogs.com/KKKEr/p/2750386.html
Copyright © 2011-2022 走看看