Java編程思想學習筆記(11)

Java編程思想學習筆記(11)

持有對象

通常,程序總是根據(jù)運行時才知道的某些條件去創(chuàng)建新對象,在此之前,不會知道所需對象的數(shù)量,甚至不知道確切的類型。為解決這個普遍的問題,需要在任意時刻和任意位置創(chuàng)建任意數(shù)量的對象,所以就不能依靠創(chuàng)建命名的引用來持有每一個對象:

如 MyType aReference;

因為你實際上不知道需要多少個這樣的引用。

大多數(shù)語言都提供某種方法來解決這個基本問題。Java有多種方式保存對象,例如數(shù)組,但是數(shù)組具有固定的尺寸,而在更一般的情況下,你在寫程序時并不知道需要多少個對象,或者是否需要更加復雜的方式來存儲對象,因此數(shù)組尺寸固定這一限制就顯得過于受限了。

Java提供了一套相當完整的容器類來解決這個問題。其中基本的類型是List,Set,Queue,Map。

這些對象被稱為集合類。

泛型和類型安全的容器

用Java SE5之前的容器的一個主要問題就是編譯器允許你向容器插入不正確的類型。

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
        }
}


上邊,Apple和Orange都放在了容器中,然后將它們?nèi)〕鰜?。正常情況下,Java編譯器會報告警告信息,因為沒有使用泛型,這里使用@SuppressWarnings注解及其參數(shù)表示只有有關(guān)“不受檢查的異?!钡木嫘畔摫灰种?。

因為ArrayList保存的是Objects對象,所以可以通過add方法將Apple對象放進容器中,還可以添加Orange對象,而且無論是在編譯期還是運行時都沒有問題。

當你用get方法取出來你覺得是Apple的對象時,你得到的只是Object的引用,必須將其轉(zhuǎn)型為Apple。否則你就會得到語法錯誤。

要想定義用來保存Apple對象的ArrayList,可以聲明ArrayList<Apple>,其中尖括號包起來的是類型參數(shù)(可以有多個),它指定了這個容器實例可以保存的類型。

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

當你指定了某個類型作為泛型參數(shù)時,你不僅可以將該確切類型的對象放置到容器中,向上轉(zhuǎn)型也可以像作用于其它類型那樣作用于泛型。

public abstract class People {

    abstract void where();
    
}


public class Chinese extends People{

    private String string;

    public Chinese() {
        this.string = "Chinese";
    }

    @Override
    void where() {
        System.out.println(string);
    }
}

public class Janpanse extends People{

    private String string;

    public Janpanse() {
        this.string = "Janpanse";
    }

    @Override
    void where() {
        System.out.println(string);
    }
}

public class GenericsAndUpcasting {

    public static void main(String[] args) {

        ArrayList<People> peoples = new ArrayList<>();

        peoples.add(new Chinese());

        peoples.add(new Janpanse());

        for (People people : peoples) {
            people.where();
        }


    }
}

基本概念

Java容器類類庫的用途是“保存對象”,并將其劃分為兩個不同的概念:

  • 1 Collection 一個獨立元素的序列,這些元素都服從一條或多條規(guī)則

  • 1.1 List必須按照插入順序保存元素

  • 1.2 Set不能有重復元素

  • 1.3 Queue按照隊列規(guī)則來確定對象產(chǎn)生的順序

  • 2 Map。一組鍵值對對象。

你應該創(chuàng)建一個具體類的對象,然后將其轉(zhuǎn)型為對應的接口,然后在其余的代碼中都使用這個接口。

Collection接口概括了序列的概念—————— 一種存放一組對象的方式。

public class SimpleCollection {
    public static void main(String[] args) {
        Collection<Integer> c = new ArrayList<Integer>();
        for(int i = 0; i < 10; i++)
            c.add(i); // Autoboxing
        for(Integer i : c)
            System.out.print(i + ", ");
    }
}

上面例子只使用了Collection,任何繼承自Collection類的對象都可以正常工作。

添加一組元素

Arrays.asList()方法接受一個數(shù)組或是一個用逗號分隔的元素列表(使用可變參數(shù)),并將其轉(zhuǎn)換為一個List對象。Collections.addAll( ) 方法接受一個Collection對象,以及一個數(shù)組或是一個用逗號分割的列表,將元素添加到Collection中。

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

//        Exception in thread "main" java.lang.UnsupportedOperationException
// list.add(21); // Runtime error because the
// underlying array cannot be resized.

    }
}

Collection的構(gòu)造器可以接受另一個Collection,用來將自身初始化。因此你可以使用
Arrays.asList()來為這個構(gòu)造器產(chǎn)生輸入。

但是Collections.addAll() 方法運行的要快很多,而且構(gòu)建一個不包含元素的Collection,然后調(diào)用Collections.addAll()這種方式是很方便的。

Collection.addAll()方法只能接受另一個Collection對象作為參數(shù),因此它不如Arrays.asList() 或者Collections.addAll()靈活。這兩個方法使用的都是可變參數(shù)列表。

也可以直接使用Arrays.asList()的輸出,將其當做List,但是在這種情況下,其底層表示是數(shù)組,因此不能調(diào)整尺寸。

class Car {
}

public class CarUseGas extends Car{
}


public class CarUseElec extends Car{
}

public class Audi extends CarUseGas{
}

public class TesLa extends CarUseElec{
}

public class BMW extends CarUseGas{
}



public class AsListInference {


    public static void main(String[] args) {

        List<Car> c1 = Arrays.asList(
                new CarUseElec(),new CarUseGas()
        );

//        不會編譯

        List<Car> c2 = Arrays.asList(
                new Audi(),new BMW()
        );

        List<Car> c3 = new ArrayList<Car>();

        Collections.addAll(c3,new Audi(),new TesLa());

        List<Car> c4 = Arrays.<Car>asList(
                new Audi(),new TesLa()
        );

//        System.out.println(c2.get(0).getClass());


    }

}

當創(chuàng)建c2時,Arrays.asList()只有 CarUseGas類型,因此它會創(chuàng)建List< CarUseGas >而不是List< Car >.

容器的打印

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


}

上面展示了Java容器類庫中的兩種主要類型,區(qū)別在于容器中每個“槽”保存的元素個數(shù)。

Collection每個槽中只能保存一個元素,Map在每個槽內(nèi)保存兩個元素。

List

先貼上將要使用的Pet類及其子類以及一些方法:

public class Individual {

    private static long counter = 0;

    private final long id = counter++;

    private String name;

    public long id(){
        return id;
    }

    public Individual(String name) {
        this.name = name;
    }

    public Individual() {
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() +
                (name == null ? "" : " " + name);
    }
}


public class Person extends Individual{

    public Person(String name) {
        super(name);
    }

    public Person() {
    }
}

public class Pet extends Individual{

    public Pet(String name) {
        super(name);
    }

    public Pet() {
    }
}

public class Cat extends Pet{

    public Cat(String name) {
        super(name);
    }

    public Cat() {
    }
}

public class Dog extends Pet{

    public Dog(String name) {
        super(name);
    }

    public Dog() {
    }
}

public class Rodent extends Pet{


//    嚙齒動物
    public Rodent(String name) {
        super(name);
    }

    public Rodent() {
    }
}

public class Manx extends Cat{


//    馬恩島貓
    public Manx(String name) {
        super(name);
    }

    public Manx() {
    }
}

public class Cymric extends Manx{

//    威爾斯貓

    public Cymric(String name) {
        super(name);
    }

    public Cymric() {
    }
}

public class EgyptianMau extends Cat{

//    埃及貓
    public EgyptianMau(String name) {
        super(name);
    }

    public EgyptianMau() {
    }
}



public class Rat extends Rodent{

    public Rat(String name) {
        super(name);
    }

    public Rat() {
    }
}

public class Hamster extends Rodent{

//    倉鼠
    public Hamster(String name) {
        super(name);
    }

    public Hamster() {
    }
}



public class Pug extends Dog{


//     哈巴狗
    public Pug(String name) {
        super(name);
    }

    public Pug() {
    }
}

public class Mouse extends Rodent{

    public Mouse(String name) {
        super(name);
    }

    public Mouse() {
    }
}

public class Mutt extends Dog{


//    雜種狗
    public Mutt(String name) {
        super(name);
    }

    public Mutt() {
    }
}

public abstract class PetCreator {

    private Random random = new Random(47);

    public abstract List<Class<? extends Pet>> types();

    public Pet randomPet(){

        int n = random.nextInt(types().size());

        try {
            return types().get(n).newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e){
            throw new RuntimeException(e);
        }
    }

    public Pet[] createArray(int size){

        Pet[] result = new Pet[size];

        for (int i = 0; i < size ; i++) {
            result[i] = randomPet();
        }

        return result;
    }


    public ArrayList<Pet> arrayList(int size){

        ArrayList<Pet> result = new ArrayList<>();

        Collections.addAll(result,createArray(size));

        return result;
    }
}


public class LiteralPetCreator extends PetCreator{

    // No try block needed.
    @SuppressWarnings("unchecked")
    public static final List<Class<? extends Pet>> allTypes =
            Collections.unmodifiableList(Arrays.asList(
                    Pet.class, Dog.class, Cat.class, Rodent.class,
                    Mutt.class, Pug.class, EgyptianMau.class, Manx.class,
                    Cymric.class, Rat.class, Mouse.class,Hamster.class));
    // Types for random creation:
    private static final List<Class<? extends Pet>> types =
            allTypes.subList(allTypes.indexOf(Mutt.class),allTypes.size());
    public List<Class<? extends Pet>> types() {
        return types;
    }
}

public class Pets {

    public static final PetCreator creator =
            new LiteralPetCreator();
    public static Pet randomPet() {
        return creator.randomPet();
    }
    public static Pet[] createArray(int size) {
        return creator.createArray(size);
    }
    public static ArrayList<Pet> arrayList(int size) {
        return creator.arrayList(size);
    }


}


有兩種類型的List:

  • ArrayList

  • LinkedList

例子:

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());

    }

//    第一行輸出展示了最初的由Pet構(gòu)成的List,與數(shù)組不同,List允許在它被創(chuàng)建之后添加元素,
//    移除元素,或者自我調(diào)整尺寸,
//    第二行,可以看到添加了一個Hamster
//    用contains方法來確定某個對象是否在列表中
//    subList允許從較大的列表中創(chuàng)建出一個片斷
//    retainAll方法有效的交集操作
}



迭代器

使用容器,必須對容器的確切類型進行編程,這樣并不會,比如你最開始是對著List編碼的,但是后來發(fā)現(xiàn)如果能夠把相同的代碼應用于Set,將會顯得更加方便,此時應該怎么做呢?或者打算從頭開始編寫通用的代碼,它們只是使用容器,不知道或者說不關(guān)心容器的類型。

迭代器可以達到這一目的,迭代器是一個對象,它的工作是遍歷并選擇序列中的對象,而不必關(guān)系該序列底層的結(jié)構(gòu)。

Java的Iterator只能單向移動,只能用來:

  • 使用方法 iterator()要求容器返回一個Iterator,Iterator將會準備好返回序列的第一個元素

  • 使用next()獲得序列的下一個元素

  • 使用hasNext()檢查序列中是否還有元素

  • 使用remove()將迭代器新近返回的元素刪除

例子:

public class SimpleIteration {

    public static void main(String[] args) {
        List<Pet> pets = Pets.arrayList(12);
        Iterator<Pet> it = pets.iterator();
        while(it.hasNext()) {
            Pet p = it.next();
            System.out.print(p.id() + ":" + p + " ");
        }
        System.out.println();
// A simpler approach, when possible:
        for(Pet p : pets)
            System.out.print(p.id() + ":" + p + " ");
        System.out.println();
// An Iterator can also remove elements:
        it = pets.iterator();
        for(int i = 0; i < 6; i++) {
            it.next();
            it.remove();
        }
        System.out.println(pets);
    }
}

考慮創(chuàng)建一個display方法,它不必知曉容器的確切類型:

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


}


display方法不包含任何有關(guān)它所遍歷的序列的類型信息,而這也顯示了Iterator的真正威力,能夠?qū)⒈闅v序列的操作與序列底層的結(jié)構(gòu)分離。

ListIterator

ListIterator是一個更加強大的Iterator的子類型。她只能用于各種List類的訪問,盡管Iterator只能向前移動,但是ListIterator可以雙向移動,它還可以產(chǎn)生相對于迭代器在列表中指向的當前位置的前一個和后一個元素的索引。

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);
    }
}

LinkedList

LinkedList也像ArrayList一樣實現(xiàn)了基本的List接口,但是它執(zhí)行某些操作(在List中間插入和移除)時比ArrayList更加高效。

LinkedList還添加了可以使其用作棧,隊列,雙端隊列的方法。

例子:

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


}


Stack

LinkedList具有能夠直接實現(xiàn)棧的所有功能的方法,因此可以直接將LinkedList當做棧使用,不過了解一下棧的實現(xiàn)可以更好的了解它的內(nèi)部機制。

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

進行演示:

public class StackTest {

    public static void main(String[] args) {
        Stack<String> stack = new Stack<String>();
        for(String s : "My dog has fleas".split(" "))
            stack.push(s);
        while(!stack.empty())
            System.out.print(stack.pop() + " ");
    }
}


public class StackCollision {

    public static void main(String[] args) {
        Stack<String> stack =
                new 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() + " ");
    }


}

Set

Set不保存重復元素。

Set具有和Collection完全一樣的接口,因此沒有額外的功能。實際上,Set就是Collection,只是行為不同。

例子:


public class SetOfInteger {

    public static void main(String[] args) {
        Random rand = new Random(47);
        Set<Integer> intset = new HashSet<Integer>();
        for(int i = 0; i < 10000; i++)
            intset.add(rand.nextInt(30));
        System.out.println(intset);
    }


}

0到29之間的10000個隨機數(shù)被添加到了Set中,其中沒有重復的數(shù)字出現(xiàn)。

并且結(jié)果也是亂序的,如果想對結(jié)果排序,一種方式是使用TreeSet代替HashSet。

public class SortedSetOfInteger {

    public static void main(String[] args) {
        Random rand = new Random(47);
        SortedSet<Integer> intset = new TreeSet<Integer>();
        for(int i = 0; i < 10000; i++)
            intset.add(rand.nextInt(30));
        System.out.println(intset);
    }


}

接下來使用contains()測試Set的歸屬性。

public class SetOperations {

    public static void main(String[] argsStr) {
            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);
    }


}

Map

下面,鍵是由Random隨機產(chǎn)生的數(shù)字,而值是該數(shù)字出現(xiàn)的次數(shù)。

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);
    }


}


下面這個使用一個String描述來查找Pet,并且使用containsKey()和containsValue()來測試一個Map,查看它是否包含某個鍵或者值。

public class PetMap {

    public static void main(String[] args) {
        Map<String, Pet> petMap = new HashMap<String,Pet>();
        petMap.put("My Cat", new Cat("Molly"));
        petMap.put("My Dog", new Dog("Ginger"));
        petMap.put("My Hamster", new Hamster("Bosco"));
        print(petMap);
        Pet dog = petMap.get("My Dog");
        print(dog);
        print(petMap.containsKey("My Dog"));
        print(petMap.containsValue(dog));
    }


}


擴展到多維:

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);
        }
    }
}

Queue

LinkedList提供了方法以支持隊列的行為,并且它實現(xiàn)了Queue接口。


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);
    }
}

PriorityQueue

優(yōu)先級隊列聲明下一個彈出元素是最需要的元素

當你在PriorityQueue上調(diào)用offer方法來插入一個對象時,這個對象會在隊列中被排序,默認的排序?qū)⑹褂脤ο笤陉犃兄械淖匀豁樞颍悄憧梢酝ㄟ^提供自己的Comparator來修改這個順序。

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);
    }


}

Collection和Iterator

Collection是描述所有序列容器的共性的接口,它可能被認為是一個“附屬接口”,因為要表示其他若干個接口的共性而出現(xiàn)的接口。另外,java.utiL.AbstractCollection提供了Collection的默認實現(xiàn),使得你可以創(chuàng)建AbstractCollection的子類型,而其中沒有不必要的代碼重復。

用迭代器而不是Collection來表示容器之間的共性,但是,這兩種方法綁定到了一起,因為實現(xiàn)Collection就意味著需要提供iterator()方法。

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

兩個版本的display方法都可以使用Map或Collection的子類型來工作,而且Collection接口和Iterator都可以將display方法以底層容器的特定實現(xiàn)解耦。

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



上面可以看出,如果你實現(xiàn)Collection,就必須實現(xiàn)iterator(),只拿實現(xiàn)iterator()與繼承AbstractCollection相比,花費的代價只是略小。

如果你的類已經(jīng)繼承其它類,那么你要實現(xiàn)Collection就必須實現(xiàn)該接口中的所有方法。

此時,繼承并提供創(chuàng)建迭代器的能力就會容易的多。


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


Foreach與迭代器

foreach語法主要用于數(shù)組,但是也可以用于任何Collection對象


public class ForEachCollections {

    public static void main(String[] args) {
        Collection<String> cs = new LinkedList<String>();
        Collections.addAll(cs,
                "Take the long way home".split(" "));
        for(String s : cs)
            System.out.print("‘" + s + "‘ ");
    }


}

上邊之所以能夠工作是因為Java SE5引入了新的Iterable接口,該接口包含一個能夠產(chǎn)生
Iterator的iterator方法,并且Iterable接口被foreach用來在序列中移動,因此如果你創(chuàng)建了任何實現(xiàn)Iterable的類,都可以將它用在foreach語句中。

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 + " ");
    }
}

iterator方法返回的是實現(xiàn)了Iterator<String>的匿名內(nèi)部類的實例,該匿名內(nèi)部類可以遍歷數(shù)組中的所有單詞。

在Java SE5中,大量的類都是Iterable類型,主要包括所有的Collection類(不包括Map類)。

下面可以顯示所有操作系統(tǒng)的環(huán)境變量:

public class EnvironmentVariables {

    public static void main(String[] args) {
        for(Map.Entry entry: System.getenv().entrySet()) {
            System.out.println(entry.getKey() + ": " +
                    entry.getValue());
        }
    }

}

foreach語句可以用于數(shù)組或者其他任何Iterable,但是這并不意味著數(shù)組肯定也是一個Iterable,而且任何自動包裝也不會自動發(fā)生

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));
    }


}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 從小到大,哪怕現(xiàn)在已到中年,最好的催眠曲一直都是老師的講課。 今天,老師在微信群發(fā)過一個鏈接,讓聽課。因為馬上要考...
    晉軒姐姐閱讀 349評論 0 1
  • 當一條 sql 語句被 SparkSqlParser 解析為一個 unresolved logicalPlan 后...
    牛肉圓粉不加蔥閱讀 2,051評論 0 2
  • 風兒不曾為草木駐足 只是搖曳的枝條證明風的來過 落花與枯萎是草木對風無聲的控訴 風吹過,沒有短暫的停留 似極了那個...
    燕子tk閱讀 477評論 4 9
  • 南南家的爸媽因為舊文件的筆誤重新辦結(jié)婚證。新的證件出來,想到歌詞,傻傻兩個人笑得多甜。 家庭聚會的時候,聚齊一座城...
    唐四月閱讀 289評論 0 0

友情鏈接更多精彩內(nèi)容