《Thanking in Java》11. 持有對象

java中提供了一套相當完整的集合類,其中基本的類型是List,Set,Queue和Map。

11.1 泛型和類型安全的容器

ArrayList<Apple>,不僅僅只是ArrayList,其中尖括號括起來的是類型參數(可以有多個),它指定了這個容器實例可以保存的類型。通過使用泛型,就可以在編譯器防止將錯誤類型的對象放置到容器中。向上轉型也可以像作用于其他類型一樣作用于泛型。

11.3 添加一組元素

Collection.addAll()
Collections.addAll()
兩者的區別:前者只能接受另一個Collection對象作為參數,后者是可變參數,且第一個參數是被添加的Collection對象。

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());
  }
} ///:~

Arrays.asList()只有Powder類型,因此它會創建List<Powder>而不是List<Snow>。
Collections.addAll工作的很好,因為它從第一個參數中了解到了目標類型是什么。
Arrays.<Snow>asList告訴了編譯器實際的目標類型應該是什么。這稱為顯示類型參數說明。

TreeSet, TreeMap,按照升序保存。
LinkedHashSet, LinkedHashMap,按照插入順序保存,同時保留了查詢速度。

11.5 List

有兩種list:
基本的ArrayList,優點是隨機訪問元素,但是在List的中間插入和刪除元素時較慢;
LinkedList,通過代價較低的在List中間進行的插入和刪除操作,提供了優化的順序訪問,LinkedList在隨機訪問方面相對比較慢。

Collections類的shuffle()方法的作用是將List中的內容隨機打亂順序。

11.6 迭代器

迭代器不必知道序列底層的機構,通常被稱為輕量級對象,創建它的代價小。迭代器統一了對容器的訪問方式。

ListIterator是一個更加強大的Iterator子類型,它只能用于各種List類的訪問。

//: 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, 1; Cymric, 3, 2; Mutt, 4, 3; Pug, 5, 4; Cymric, 6, 5; Pug, 7, 6; Manx, 8, 7;
7 6 5 4 3 2 1 0
[Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx]
[Rat, Manx, Cymric, Cymric, Rat, EgyptianMau, Hamster, EgyptianMau]
*///:~

ListIterator可以雙向移動,獲取當前位置的前后元素索引,set()方法替換它訪問過的最后一個元素,通過調用ListIterator(n)方法創建一個一開始就指向列表索引為n的元素處得ListIterator。

11.7 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
*///:~

11.9 Set

查找成為了Set中最重要的操作,通常會選擇一個HashSet的實現,它專門對快速查找進行了優化。

在TreeSet的構造器里可以傳入比較器。

11.10 Map

使用containsKeys()和containsValue()來測試一個Map,以便查看它是否包含某個鍵某個值。

Map的值可以是其他容器

entrySet()返回一個實現Map.Entry接口的對象集合。Entry是Map中用來保存一個鍵值對的,而Map實際上就是多個Entry的集合。

11.11 Queue

LinkedList提供了方法以支持隊列的行為,并且它實現了Queue接口,因此LinkedList可以用作Queue的一種實現。通過將LinkedList向上轉型為Queue。

offer方法將一個元素插入到隊尾,或者返回false。peek和element都是不移除的情況下返回隊頭,peek在隊空時返回null。poll和remove方法將移除并返回隊頭,poll在空的時候返回null。

自動裝箱機制可以將int轉換成queue所需的Interger對象。

優先級隊列聲明下一個彈出元素時最需要的元素(具有最高的優先級)。

PriorityQueue上調用offer方法來插入一個對象時,這個對象會在隊列中被排序。某認的排序將使用對象在隊列中的自然順序,但是你可以通過提供自己的Comparator來修改這個順序。PriorityQueue可以確保當你調用peek、poll、remove方法時,獲取的元素將是隊列中優先級最高的元素。

//: 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
*///:~

最小值擁有最高的優先級,空格比子目的優先級更高。可以使用Comparator對象改變排序

11.12 Collection和Iterator

Collection是描述所有序列容器的共性的根接口,通過針對接口而非具體實現來編寫代碼,我們的代碼可以應用于更多的對象類型,與底層容器的特定實現解耦。

一共有三種方式實現可迭代:

  1. 繼承AbstractCollection;
  2. 實現Collection;
  3. 繼承并提供創建迭代器,生成Iterator。將隊列與消費隊列的方法連接在一起耦合度最小,并且與實現Collection相比,它在序列類上所施加的約束也少得多。

11.13 Foreach與迭代器

如果創建了任何實現Iterable的類,都可以將它用于foreach語句中。主要包括所有的Collection類,但是不包括各種Map。

foreach語句可以用于數組,這并不意味著數組肯定也是一個Iterable,不存在任何從數組到Iterable的自動轉換,必須手動執行這種轉換,Arrays.asList()。

Arrays.asList()和構造方法區別:
Arrays.asList()的輸出傳遞給了ArrayList的構造器,這將創建一個引用對象,因此打亂這些引用不會修改該數組;但是如果直接使用Arrays.asList(),產生的List對象會使用底層數組作為其物理實現,只要修改這個List,底層的數組也會隨之修改。

容器不能持有基本類型,但是自動包裝機制會仔細地執行基本類型到容器中所持有的包裝器類型之間的雙向轉換。

Queue和Stack的行為,有LinkedList提供支持。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • java筆記第一天 == 和 equals ==比較的比較的是兩個變量的值是否相等,對于引用型變量表示的是兩個變量...
    jmychou閱讀 1,516評論 0 3
  • 概述 Java集合框架由Java類庫的一系列接口、抽象類以及具體實現類組成。我們這里所說的集合就是把一組對象組織到...
    absfree閱讀 1,271評論 0 10
  • 以下是《瘋狂Java講義》中的一些知識,如有錯誤,煩請指正。 集合概述 Java集合可以分為Set、List、Ma...
    hainingwyx閱讀 554評論 0 1
  • 3.3 集合 一方面, 面向對象語言對事物的體現都是以對象的形式,為了方便對多個對象的操作,就要對對象進行存儲。另...
    閆子揚閱讀 743評論 0 1
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,738評論 18 399