Thinking in java 第11章 持有對象 筆記+習題

Thinking in java 第11章 持有對象

學習目錄


11.1 泛型和類型安全的容器

1. 當你制定了某個類型作爲泛型參數時,你並不僅限於只能將該確切類型的對象放置到容器中。向上轉型也可一樣作用於泛型。

 

11.2 基本概念

1. Java容器被劃分爲兩個概念:

  • Collection:一個獨立元素的序列,這些元素都服從一條或多條規則。
  • Map:一組成對的“鍵值對”對象,允許你使用鍵來查找值。

 

11.3 添加一元素

1. Arrays.asList() 方法接收一個數組或是一個用逗號分隔的元素列表,並將其轉換爲一個List對象。

2. Collections.addAll() 方法接收一個Collection對象,以及一個數組或是一個用逗號分隔的列表。

Collections<Integer> collection = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5));
Integer[] moreInts = {6,7,8,9,10};
collection.addAll(Array.asList(moreInts));

Collections.addAll(collection, 11, 12, 13, 14, 15);
Collections.addAll(collection, moreInts);

List<Integer> list = Arrays.asList(16,17,18,19,20);
list.set(1,99);
//! list.add(21); underlying array cannot be resized

3. 方法2只能接受Collection,因此沒有方法1靈活;而方法1返回的底層是數組,不能改變大小(即不能add或delete)

 

11.4 容器的打印

1. Set類型:

  • HashSet使用相當複雜的方式來存儲元素,這種技術是最快的獲取元素方式,因此順序無實際意義。
  • TreeSet按照比較結果的升序保存對象。
  • LinkedHashSet按照被添加的順序保存對象。

2. Map類型(Map.put(key, value); Map.get(key))

  • HashMap提供了最快的查找技術,沒有按照任何明顯順序來保存。
  • TreeMap按照比較結果的升序保存鍵。
  • LinkedHashMap按照插入順序保存鍵,同時還留下了HashMap的查詢速度。

 

11.5 List

1. 分爲ArrayListLinkedList,區別即順序表和鏈表的區別。

2. 用contains()方法判斷某個對象是否在列表中。

3. 用remove()方法移除某個對象。

4. 用indexOf()方法查詢索引編號。

5. 比較要用到equals()方法,最好都重寫。

6. 用subList()方法獲取子集。

7. containsAll()、Collections.shuffle()、Collections.sort()、retainAll() 交集、removeAll()、set()、isEmpty()、clear()、toArray()

 

11.6 迭代器

1. 迭代器通常被稱爲輕量級對象:創建它的代價小。因此迭代器有很多奇怪的限制,例如Java的Iterator只能單向移動,只能用來:

  • 使用iterator()要求容器返回一個Iterator。Iterator將準備好返回序列的第一個元素。
  • 使用next()獲得下一個元素。
  • 使用hasNext()檢查是否還有元素。
  • 使用remove()將迭代器將最新的元素刪除,所以必須先調用next()再用remove()。

2. 用Iterator<T> it 就能不管T的容器類型。

3. ListIteratorIterator的子類,可以雙向移動,還可以返回指向當前元素的前一個和後一個元素的索引,且可使用set()替換訪問過的最後一個元素。 方法爲:hasNext()、hasPrevious()、next()、previous()、nextIndex()、previousIndex()、set()

 

11.7 LinkedList

1. LinkedList添加了可以使其用作棧、隊列或雙端隊列的方法,這些方法中有些彼此之間只是名字差異或只有輕微差異。如:

  • getFirst()和element()完全一樣,返回表頭元素,若空拋出異常,而peek()方法在空時返回null;
  • removeFirst()和remove()完全一樣,一處表頭,若空拋出異常,而poll()方法在空時返回null;
  • addFirst();add()和addLast()相同;removeLast();

 

11.8 Stack

1. push(T v)、peek()、pop()、empty()、toString()

 

11.9 Set

1. Set具有與Collection完全一樣的接口,因此沒有任何額外功能,不像前面有兩個不同的List。

2. HashSet使用的是散列數組;TreeSet使用的是紅黑樹;LinkedHashSet也使用了散列,並用鏈表維護插入順序。

3. 在TreeSet中如果想不分大小寫進行從小到大排序,則用 Set<String> words = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);

4. 方法爲 add() 、 remove() 等。

 

11.10 Map

1. 添加時如下:

Integer freq = m.get(r);
m.put(r, freq == null? 1 : freq+1);

2. 可擴展到多維,即value可以使其他容器。

3. 遍歷時如下:

for(T t : map.keySet())
    print(t + ": " + map.get(t));

 

11.11 Queue

1. 可以將LinkedList向上轉型爲Queue,因爲其實現了Queue接口。故可寫成 Queue<Integer> q = new LinkedList<Integer>();

2. 方法:offer() 插入隊尾、peek()/element()、poll()/remove() 等。

3. PriorityQueue<T>,Collections.reverseOrder()產生反序的Comparator。

4. 如果你想在優先隊列中使用自己的類,就必須包括額外的功能以產生自然排序,或者必須提供自己的Comparator。

 

11.12 Collection和Iterator

1. 在基於兩個接口都能實現時(例如遍歷輸出),用Collection方便一些,因爲它是Iterable的,用Foreach語句更加清晰。

2. 當要實現一個不是Colletion的外部類時,用Iterator更好。因爲實現Collection必須要實現iterator(),而且還必須要實現其他一些方法。

 

11.13 Foreach迭代器

1. 如果你創建了任何實現Iterable的類,都可以將它用於Foreach語句中。

class A implements Iterable<T> {
    protected T[] words = ...;
    public Iterable<T> iterator() {
        return new Iterable<T>() {
            private int index = 0;
            public boolean hasNext() { return index < words.length; }
            public T next() { return words[index++]; }
            public void remove() {...}
        }
    }
}

2. Map的Foreach遍歷如下:

for(Map.Entry entry : m.entrySet()) {
    print(entry.getKey(), entry.getValue());
}

3. 可以通過適配器方法,在類中添加用於不同作用的迭代方法。

 


習題

練習1:創建一個新類Gerbil(沙鼠),包含int gerbilNumber,在構造器中初始化它.添加一個方法hop(),用以打印沙鼠的號碼以及它正在跳躍的信息。創建一個ArrayList,並向其中添加一串Gerbil對象。使用get()遍歷List,並且對每個Gerbil調用hop()。

package Chapter11;

import java.util.ArrayList;
import java.util.Arrays;

public class E1 {
    public static void main(String[] args) {
        ArrayList<E1Gerbil> list = new ArrayList<E1Gerbil>(Arrays.asList(new E1Gerbil(1), new E1Gerbil(2),
        new E1Gerbil(3), new E1Gerbil(4)));
        for(E1Gerbil e : list) {
            e.hop();
        }
    }
}

class E1Gerbil {
    private int gerbilNumber;

    public E1Gerbil(int gerbilNumber) {
        this.gerbilNumber = gerbilNumber;
    }

    public int getGerbilNumber() {
        return gerbilNumber;
    }

    public void setGerbilNumber(int gerbilNumber) {
        this.gerbilNumber = gerbilNumber;
    }

    public void hop() {
        System.out.println("" + gerbilNumber + " is jumping");
    }
}

/*
1 is jumping
2 is jumping
3 is jumping
4 is jumping
*/

練習2:修改SimpleCollection.java,使用Set來表示c。

略。

練習3:修改innerclasses/Sequence.java,使你可以向其中添加任意數量的元素。

略。把Object[] 改爲ArrayList<Object> ,並用相應的方法替換即可。(P192)

練習4:創建一個生成器類,它可以在每次調用其next()方法時,產生你最喜歡的電影的名字。在電影名列表的名字用完之後,循環到該列表的開始處。使用這個生成器來填充數組、ArrayList、LinkedList、HashSet、LinkedHashSet和TreeSet,然後打印每個容器。

package Chapter11;

import java.util.*;

public class E4 {
    public static void main(String[] args) {
        System.out.println("ArrayList " + new E4Generator().fill(new ArrayList<String>()));
        System.out.println("LinkedList " + new E4Generator().fill(new LinkedList<String>()));
        System.out.println("HashSet " + new E4Generator().fill(new HashSet<String>()));
        System.out.println("LinkedHashSet " + new E4Generator().fill(new LinkedHashSet<String>()));
        System.out.println("TreeSet " + new E4Generator().fill(new TreeSet<String>()));
    }
}

class E4Generator {
    private String[] movies = {"ZZ", "BB", "CC", "AA", "FF", "EE"};
    private int maxLength = 6;
    private int i = 0;

    public String next() { return movies[(i++)%maxLength]; }

    public Collection<String> fill(Collection<String> c) {
        for(int j = 0; j < 8; j++) {
            c.add(next());
        }
        return c;
    }
}

/*
ArrayList [ZZ, BB, CC, AA, FF, EE, ZZ, BB]
LinkedList [ZZ, BB, CC, AA, FF, EE, ZZ, BB]
HashSet [ZZ, BB, CC, AA, FF, EE]
LinkedHashSet [ZZ, BB, CC, AA, FF, EE]
TreeSet [AA, BB, CC, EE, FF, ZZ]
*/

練習5-練習6:修改LIstFeatures.java,讓它用Integer/String而不是Pet,並解釋在結果上有何不同。

略。

練習7:創建一個類,然後創建一個用你的類的對象進行過初始化的數組。通過使用subList()方法,創建你的List子集,然後在你的List中移除這個子集。

package Chapter11;

import java.util.*;

public class E7 {
    public static void main(String[] args) {
        E7A[] es = new E7A[] {new E7A(1), new E7A(2), new E7A(3), new E7A(4)};
        List<E7A> list = new ArrayList<E7A>(Arrays.asList(es));
        List<E7A> list2 =  list.subList(1, 2);
        System.out.println(list2);
        list.removeAll(list2);
        System.out.println(list);
    }
}

class E7A {
    int a;

    public E7A(int a) {
        this.a = a;
    }

    @Override
    public String toString() {
        return Integer.toString(a);
    }
    
}

/*
[2]
[1, 3, 4]
*/

練習8:修改練習題1,以便調用hop()時使用Iterator遍歷List。

package Chapter11;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;

public class E1 {
    public static void main(String[] args) {
        ArrayList<E1Gerbil> list = new ArrayList<E1Gerbil>(Arrays.asList(new E1Gerbil(1), new E1Gerbil(2),
        new E1Gerbil(3), new E1Gerbil(4)));
        Iterator<E1Gerbil> iter = list.iterator();
        while(iter.hasNext()) {
            iter.next().hop();
        }
    }
}

class E1Gerbil {
    private int gerbilNumber;

    public E1Gerbil(int gerbilNumber) {
        this.gerbilNumber = gerbilNumber;
    }

    public int getGerbilNumber() {
        return gerbilNumber;
    }

    public void setGerbilNumber(int gerbilNumber) {
        this.gerbilNumber = gerbilNumber;
    }

    public void hop() {
        System.out.println("" + gerbilNumber + " is jumping");
    }
}

/*
1 is jumping
2 is jumping
3 is jumping
4 is jumping
*/

練習9:修改innerclasses/Sequence.java,使得在Sequence中,用Iterator取代Selector。

略。用個方法return collection.iterator()即可。

練習10:修改第8章中的練習9,使其使用一個ArrayList來存放Rodents,並使用一個Iterator來訪問Rodent序列。

略。

練習11:寫一個方法,使用Iterator遍歷Collection,並打印容器中每個對象的toString()。填充各種類型的Collection,然後對其使用此方法。

同P227代碼。略。

練習12:創建並組裝一個List<Integer>,然後創建第二個具有相同尺寸的List<Integer>,並使用ListIterator讀取第一個List中的元素,然後再將它們以反序插入到第二個列表中。

package Chapter11;

import java.util.*;

public class E12 {
    public static void main(String[] args) {
        List<Integer> list1 = new ArrayList<>(Arrays.asList(1,2,3,4,5,6));
        List<Integer> list2 = new ArrayList<>();
        ListIterator<Integer> iter = list1.listIterator();
        while(iter.hasNext()) iter.next();
        while(iter.hasPrevious()) list2.add(iter.previous());
        System.out.println(list2);
    }
}

/*
[6, 5, 4, 3, 2, 1]
*/

直接寫 ListIterator<Integer> iter = list1.listIterator(list1.size()); 更好。

練習13:在innerclasses/GreenhouseController.java示例中,Controller類使用的是ArrayList,修改代碼,用LinkedList替換之,並使用Iterator來循環遍歷事件集。

略。簡單應用。

練習14:創建一個空的LinkedList<Integer>,通過使用ListIterator,將若干個Integer插入這個List中,插入時,總是將它們插入到List的中間。

package Chapter11;

import java.util.*;

public class E14 {
    public static void main(String[] args) {
        List<Integer> list = new LinkedList<Integer>();
        for(int i = 0; i < 10; i++) {
            ListIterator iter = list.listIterator(list.size()/2);
            iter.add(i);
        }
        System.out.println(list);
    }
}

/*
[1, 3, 5, 7, 9, 8, 6, 4, 2, 0]
*/

練習15:棧在編程語言中經常用來對錶達式求值。請使用net.mindview.util.Stack對下面表達式求值,其中“+”表示“將後面的字母壓進棧”,而“-”表示“彈出棧頂字母並打印它”:“+U+n+c---+e+r+t---+a-+i-+n+t+y---+ -+r+u--+l+e+s---”。

package Chapter11;

import java.util.Stack;

public class E15 {
    public static void main(String[] args) {
        String s = "+U+n+c---+e+r+t---+a-+i-+n+t+y---+ -+r+u--+l+e+s---";
        Stack<Character> stack = new Stack<>();
        for(int i = 0; i < s.length(); i++) {
            if(s.charAt(i) == '+') stack.push(s.charAt(++i));
            else System.out.print(stack.pop());
        }
        System.out.println();
    }
}

/*
cnUtreaiytn ursel
*/

練習16:創建一個元音字母Set。對UniqueWords.java操作,計數並顯示在每一個輸入單詞中的元音字母數量,並顯示輸入文件中的所有元音字母的數量總和。

沒有文件,簡單應用。略。

練習17:使用練習1中的Gerbil類,將其放入Map中,將每個Gerbil的名字String(鍵)與每個Gerbil(值)關聯起來。爲keySet()獲取Iteratror,使用它遍歷Map,針對每個“鍵”查詢Gerbil,然後打印出“鍵”,並讓gerbil執行hop()。

package Chapter11;

import java.util.*;
import Chapter11.E1Gerbil;

public class E17 {
    public static void main(String[] args) {
        ArrayList<E1Gerbil> list = new ArrayList<E1Gerbil>(Arrays.asList(new E1Gerbil(1), new E1Gerbil(2),
                new E1Gerbil(3), new E1Gerbil(4)));
        String name = "GG";
        Map<String, E1Gerbil> m = new HashMap<>();
        for(int i = 1; i < 5; i++) m.put(name+i, list.get(i-1));
        for(String s : m.keySet()) m.get(s).hop();
    }
}

/*
1 is jumping
3 is jumping
2 is jumping
4 is jumping
*/

練習18:用鍵值對填充一個HashMap。打印結果,通過散列碼來展示其排序。抽取這些鍵值對,按照鍵進行排序,並將結果置於一個LinkedHashMap中。展示其所維護的插入排序。

package Chapter11;

import java.util.*;

public class E18 {
    public static void main(String[] args) {
        Map<String, Integer> m = new HashMap<>();
        m.put("asd",1);
        m.put("zsdf",2);
        m.put("bgfd",3);
        System.out.println(m);
        List<String> strings = new LinkedList<>(m.keySet());
        Collections.sort(strings);
        Map<String, Integer> m2 = new LinkedHashMap<>();
        for(String s : strings) {
            m2.put(s, m.get(s));
        }
        System.out.println(m2);
    }
}


/*
{zsdf=2, asd=1, bgfd=3}
{asd=1, bgfd=3, zsdf=2}
*/

練習19:使用HashSet和LinkedHashSet重複前一個練習。

這也能重複?略。

練習20:修改練習16,使得你可以跟蹤每一個元音字母出現的次數。

略。

練習21:通過使用Map<String,Integer>,遵循UniqueWords.java的形式來創建一個程序,它可以對一個文件中出現的單詞計數。使用帶有第二個參數**String.CASE_INSENSITIVE_OREDER的Collection.sort()方法對結果進行排序(將產生字母序),然後顯示結果。

package Chapter11;

import java.util.*;

public class E21 {
    public static void main(String[] args) {
        String[] s = "Can you can can a can?".split(" ");
        Map<String, Integer> map = new HashMap<>();
        for(String i : s) {
            Integer temp = map.get(i);
            map.put(i, (temp==null?1:temp+1));
        }
        List<String> list = new ArrayList<>(map.keySet());
        Collections.sort(list, String.CASE_INSENSITIVE_ORDER);
        for(String i : list) {
            System.out.println(i + " : " + map.get(i));
        }
    }
}

/*
a : 1
Can : 1
can : 2
can? : 1
you : 1
*/

練習22:修改前一個練習,使其用一個包含有一個String域和一個計數域的類來存儲每一個不同的單詞,並使用一個由這些對象構成的Set來維護單詞列表。

package Chapter11;

import java.util.Objects;
import java.util.*;

public class E22 {
    public static void main(String[] args) {
        String[] s = "Can you can can a can?".split(" ");
        Set<E22A> set = new HashSet<>();
        for(String i : s) {
            E22A e = new E22A(i);
            if(set.contains(e)) {
                Iterator<E22A> iter = set.iterator();
                while(iter.hasNext()) {
                    E22A tempe = iter.next();
                    if(tempe.equals(e)) tempe.add();
                }
            }
            else {
                e.setA(1);
                set.add(e);
            }
        }
        for(E22A e : set) {
            System.out.println(e.getS() + " : " + e.getA());
        }
    }
}

class E22A {
    private String s;
    private int a;

    public void add() { a++; }

    public E22A(String s) {
        this.s = s;
    }

    public String getS() {
        return s;
    }

    public void setS(String s) {
        this.s = s;
    }

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        E22A e22A = (E22A) o;
        return Objects.equals(s, e22A.s);
    }

    @Override
    public int hashCode() {
        return Objects.hash(s);
    }
}

/*
a : 1
can? : 1
Can : 1
can : 2
you : 1
*/

要重寫equals再調用contains()。

練習23:從Statistics.java開始,寫一個程序,讓它重複做測試,觀察是否某個數字比別的數字出現的次數多。

package Chapter11;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class E23 {
    public static void main(String[] args) {
        Random r = new Random();
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < 10000; i++) {
            int temp = r.nextInt();
            Integer in = map.get(temp);
            map.put(temp, (in == null ? 1 : in+1));
        }
        for(Integer i : map.keySet()) System.out.println(""+i+" : "+map.get(i));
    }
}

....

都只出現一次的樣子。

練習24:使用String“鍵”和你選擇的對象填充LinkedHashMap。然後從中提取鍵值對,以鍵排序,然後重新插入此Map。

略。和前面差不多。

練習25:創建一個Map<String,ArrayList<Integer>>,使用net.mindview.TextFile來打開一個文本文件,並一次讀入一個單詞。在讀入單詞時對它們進行計數,並且對於文件中的每一個單詞,都在ArrayList<Integer>中記錄下與這個詞相關聯的單詞計數。實際上,它記錄的是該單詞在文件中被發現的位置。

略。計數就是ArraList.size()

練習26:拿到前一個練習中所產生的Map,並按照它們在最初的文件中出現的順序重新創建單詞順序。

略。最後再遍歷一遍用LinkedHashSet存儲。

練習27:寫一個稱爲Command的類,它包含一個String域和一個顯示該String的operation()方法。寫第二類,它具有一個使用Command對象來填充一個Queue並返回這個對象的方法。將填充後的Queue傳遞給第三個類的一個方法,該方法消耗掉Queue中的對象,並調用它們的operation()方法。

package Chapter11;

import java.util.*;

public class E27 {
    public static void main(String[] args) {
        E27C.func2();
    }
}

class E27A {
    private String s;

    public void operation() {
        System.out.println("this is " + s);
    }

    public E27A(String s) {
        this.s = s;
    }
}

class E27B {
    public static Queue<E27A> func1() {
        return new LinkedList<>(Arrays.asList(new E27A("aaa"), new E27A("bbb"), new E27A("ccc")));
    }
}

class E27C {
    public static void func2() {
        Queue<E27A> q = E27B.func1();
        while(!q.isEmpty()) {
            q.poll().operation();
        }
    }
}

/*
this is aaa
this is bbb
this is ccc
*/

練習28:用由java.util.Random創建的Double值填充一個PriorityQueue(用offer())方法,然後使用poll()移除並顯示它們。

略。同上。

練習29:創建一個繼承自Object的簡單類,它不包含任何成員,展示你不能將這個類的多個示例成功地添加到一個PriorityQueue中。這個問題將在第17章中詳細解釋。

問號。看完17章再來看看能不能答。

練習30:修改CollectionSequeuece.java,使其不要繼承AbstractCollection,而是實現Collection。

略。直接實現Collection<Pet>接口要同時實現裏面的多餘的方法。

練習31:修改polymorphism/shape/RandomShapeGenerator.java,使其成爲一個Iterable。你需要添加一個接受元素數量爲參數的構造器,這個數量是指在停止之前,你想用迭代器生成的元素的數量。驗證這個程序可以工作。

略。

練習32:按照MultiIterableClass示例,在NonCollectionSequence.java中添加reversed()和randomized()方法,並讓NonCollectionSequence實現Iterable。然後在foreach語句中展示所有的使用方式。

略。同P244。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章