Java編程思想之十一 持有對象

若是一個程序只包含固定數量的且其生命期都是已知的對象,那麼這是一個很是簡單的程序。java

11.1 泛型和類型安全的容器

使用ArrayList:建立一個實例,用add()插入對象,而後用get()訪問對象,此時須要使用索引,像數組同樣,但不是要[]。程序員

import java.util.ArrayList;
public class ApplesAndOrangesWithoutGenerice
{
    @SuppressWarnings("unchecked")//不受檢查異常的警告
    public static void main(String[] args)
    {
        ArrayList apples = new ArrayList();
        for (int i = 0; i < 3; i++)
        {
            apples.add(new Apple());
        }
        apples.add(new Orange());
        for (Object obj : apples)
        {
            System.out.println(((Apple) obj).id());
        }
    }
}
class Apple
{
    private static long counter;
    private final long id = counter++;

    public long id()
    {
        return id;
    }
}
class Orange
{
}

ArrayList保存的是Object。
要想定義保存特定類型的ArrayLIst可使用泛型<>。正則表達式

import java.util.ArrayList;

public class ApplesAndOrangesWithoutGenerice
{
    @SuppressWarnings("unchecked")//不受檢查異常的警告
    public static void main(String[] args)
    {
        ArrayList<Apple> apples = new ArrayList();
        for (int i = 0; i < 3; i++)
        {
            apples.add(new Apple());
        }
        //apples.add(new Orange());
        for (Object obj : apples)
        {
            System.out.println(((Apple) obj).id());
        }
    }
}

class Apple
{
    private static long counter;
    private final long id = counter++;

    public long id()
    {
        return id;
    }
}

class Orange
{

}

當指定了某個類型做爲泛型參數時,並不只限於只能將該確切類型的對象放置到容器中,向上轉型也能夠像做用於其餘類型同樣做用於泛型。數據庫

import java.util.ArrayList;
public class ApplesAndOrangesWithoutGenerice
{
    @SuppressWarnings("unchecked")//不受檢查異常的警告
    public static void main(String[] args)
    {
        ArrayList<Apple> apples = new ArrayList();
        for (int i = 0; i < 3; i++)
        {
            apples.add(new A());
            apples.add(new B());
            apples.add(new C());
        }
        //apples.add(new Orange());
        for (Object obj : apples)
        {
            System.out.println(((Apple) obj).id());
        }
    }
}
class Apple
{
    private static long counter;
    private final long id = counter++;
    public long id()
    {
        return id;
    }
}
class A extends Apple
{
}
class B extends Apple
{
}
class C extends Apple
{
}
class Orange
{

}

11.2 基本概念

Java容器類類庫的用途是保存對象,並將其劃分爲兩個不一樣的概念:編程

  • Collection。一個獨立元素序列,這些元素都服從一條或多條規則。List必須按照插入的順序保存元素,而set不能保存重複元素。
  • Map。一組成對的鍵值對對象,容許你使用鍵來查找值。映射表容許咱們使用另外一個對象來查找某個對象,它也被稱爲關聯數組,由於它將對象於另一個對象關聯到了一塊兒。或者被稱爲字典
List<Type> types=new ArrayList<Type>()

ArrayList被向上轉型爲List。
Conllection接口歸納了序列的概念——一種存放一組對象的方式。數組

import java.util.ArrayList;
import java.util.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);
        }
        for (int i : c)
        {
            System.out.println(i);
        }
    }
}

11.3 添加一組元素

Arrays.asList()方法接受一個數組或一個用逗號分隔的元素列表,並將其轉換爲一個List對象。Collections.addAll()方法接受一個Collection對象,以及一個數組或一個用逗號分隔的列表,將元素添加到Collection中。安全

import java.util.*;
public class AddingGroups
{
    public static void main(String[] args)
    {
        Collection<Integer> collection = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4));
        Integer[] moreInts = {6, 7, 8, 9};
        collection.addAll(Arrays.asList(moreInts));
        Collections.addAll(collection,12,13,14);  Collections.addAll(collection,moreInts);
        List<Integer> list=Arrays.asList(13,14,15);
    }
}

Collection.addAll()成員方法只能接受一個Collection對象做爲參數,所以不如Arrays.asList()和Collections.addAll()靈活,着兩個方法均可以是可變參數。
能夠直接使用Arrays.asList()的輸出,將它當成List,但在這種狀況下,地城是數組。數據結構

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class AsListInference
{
    public static void main(String[] args)
    {
        List<Snow> snow1 = Arrays.asList(new P(), new S());
        List<Snow> snow2 = Arrays.asList(new P(), new S(), new L());
        List<Snow> snow3 = Arrays.asList(new H(), new L());
        List<Snow> snow4 = new ArrayList<Snow>();
        Collections.addAll(snow4, new P(), new L());
        List<Snow> snow5 = Arrays.<Snow>asList(new L());//顯示類型參數說明
    }
}

class Snow{}

class P extends Snow{}

class L extends P{}

class H extends P{}

class S extends Snow{}

11.4 容器的打印

import java.util.*;

public class PrintingContainers
{
    static Collection fill(Collection<String> coll)
    {
        coll.add("rat");
        coll.add("cat");
        return coll;
    }

    static Map fill(Map<String, String> map)
    {
        map.put("rat", "r");
        map.put("cat", "c");
        return map;
    }

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

        System.out.println(fill(new HashMap<String,String>()));
        System.out.println(fill(new TreeMap<String,String>()));
        System.out.println(fill(new LinkedHashMap<String,String>()));
    }
}

Collection在每一個槽中只能保存一個元素,此類容器還包括List。Map每一個槽內保存兩個元素,鍵和值相關聯的值。
ArrayList和LinkedList都是List類型,從輸出能夠看出,它們都是按照被插入的順序保存元素的。
二者的不一樣之處不只在於執行某寫類型的操做時的性能。並且LinkedList包括的操做也多於ArrayList。
HashSet、TreeSet和LinkedHashSet都是Set類型。
Map使得你能夠用鍵來查找對象,就像一個簡單的數據庫。鍵所關聯的對象稱爲值。
HashSet同樣,HashMap也提供了最快的查找技術,也沒有按照任何明顯的順序來保存元素。TreeMap按照比較結果的升序保存鍵,而LinkedHashMap則按照插入順序保存鍵,同時還保留了HashMap的查詢速度。併發

11.5 List

List能夠將元素維護在特定的序列中。List接口在Collection的基礎上添加大量的方法,使得能夠在List的中間插入和移除元素。
有兩種類型的List:app

  • 基本的ArrayList,它長於隨機訪問元素,但使在List的中間插入和移除元素時較慢。
  • LinkedList,它經過代價較低的在List中間進行的插入和刪除操做,提供了優化的順序訪問。LinkedList在隨機訪問方面比較慢,可是它的特性集較ArrayList更大。

11.6 迭代器

持有事物是容器最基本的工做,要使用容器,必須對容器的確切類型編程。
若是隻要使用容器,不知道或者不關心容器的類型,要如何不重寫代碼就能夠應用於不一樣類型的容器?
使用迭代器:迭代器是一個對象,它的工做是遍歷並選擇序列中的對象,而客戶端程序員不須要指定序列底層的結構。迭代器一般被稱爲輕量級對象:建立它的代價很小,常常能夠看到對迭代器的奇怪限制。
好比Java中的Iterator只能單向移動。

 

接受對象容器並傳遞它,從而再每一個對象上都執行操做。
建立一個display方法,沒必要知道容器的確切類型:

 

display方法不包含任何有關它遍歷的序列的類型信息:可以將遍歷序列的操做與序列底層的結構分離。因此,迭代器統一了對容器的訪問方式。

11.6.1 ListIterator

ListIterator是一個更增強大的Iterator的子類型,它只能用於各類List類訪問。但ListIterator能夠雙向移動。

 

11.7 LinkedList

LinkedList也像ArrayList同樣實現了基本的List接口,可是它執行某些操做比ArrayList更高效,但再隨機訪問操做要差一些。

 

11.8 Stack

棧一般是指後進先出的容器。有時也被稱爲疊加棧,由於最後壓入棧的元素,第一個彈出棧。
直接將LinkedList做爲棧使用。

import java.util.LinkedList;

public class Stack<T>
{
    LinkedList<T> storage=new LinkedList<T>();
    public void push(T t){storage.addFirst(t);}//插入一個元素
    public T peek(){return storage.getFirst();}//獲取第一個元素
    public T pop(){return storage.removeFirst();}//移除第一個元素
    public boolean empty(){return storage.isEmpty();}//是否還有元素
    public String toString(){return storage.toString();}
}

建立一個新的Stack類:

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.println(stack.pop());
    }
}

11.9 Set

Set不保存重複的元素。
Set具備與Collection徹底同樣的接口。實際上Set就是Collection,只是行爲不一樣。Set基於對象的的值來肯定歸屬性。

import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;

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<10;i++)
            intset.add(rand.nextInt(30));
        for(int i:intset)
            System.out.println(i);
    }
}

HashSet所維護的順序與TreeSet或LinkedHashSet都不一樣。TreeSetj將元素存儲再紅——黑樹數據結構中,而HashSet使用的是散列函數。LinkedHashList由於查詢速度的緣由也使用了散列。
對結構排序,使用TreeSet來代替HashSet

import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;

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

使用contains()測試Set歸屬性

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

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".split(" "));
        set1.add("m");
        System.out.println("m:"+set1.contains("m"));
        System.out.println("h:"+set1.contains("h"));
    }
}

打開文件,並將其讀入一個Set中。
TextFile繼承自List< Sting >,其構造器將打開文件,並更具正則表達式將其斷開。
若是要按照字母排序,能夠向TreeSet構造器傳入String.CASE_INSENTIVE_ORDER比較器。

import net.mindview.util.TextFile;

import java.util.Set;
import java.util.TreeSet;

public class UniqueWordsAlphabetic
{
    public static void main(String[] args){
        Set<String> words=new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
        words.addAll(new TextFile("第十一章持有對象\\src\\SimpleCollection.java","\\W+"));
        System.out.println(words);
    }
}

11.10 Map

將對象映射到其餘對象的能力是一種解決編程問題的殺手鐗。

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MapOfList
{
    public static Map<Integer, List<? extends Person>> petPeople = new HashMap<Integer, List<? extends Person>>();
    static {
        petPeople.put(1, Arrays.asList(new Man("aaa"),new WoMan("bbb")));
    }
    public static void main(String[] args){
        System.out.println(petPeople.keySet());
        System.out.println(petPeople.values());
        System.out.println(petPeople);
    }
}
class Person
{
    private String name;
    Person(String name){
        this.name=name;
    }
}
class Man extends Person
{
    public Man(String name){
        super(name);
    }
}
class WoMan extends Person
{
    WoMan(String name){
        super(name);
    }
}

Map能夠返回它的鍵的Set,它的值Collection,或者它的鍵值對的Set。

11.11 Queue

隊列是一個典型的先進先出的容器。隊列在併發編程中特別重要,由於它們能夠安全地將對象從一個任務傳輸給另外一個任務。
LinkedLisk提供了方法以支持隊列的行爲,而且它實現了Queue接口,所以LinkedLisk能夠用做Queue的一種實現。

import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;

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 ran=new Random();
        for(int i=0;i<10;i++)
            queue.offer(ran.nextInt(i+10));
        printQ(queue);
        Queue<Character> qc=new LinkedList<Character>();
        for (char c:"Brontosaurus".toCharArray())
            qc.offer(c);
        printQ(qc);
    }
}

offer()方法將一個元素插入到隊尾,或者返回false。peek()和element()都將在不移除的狀況下返回對頭。poll()和remove()方法將移除並返回對頭。

11.11.1 PriorityQueue

優先級隊列聲明下一個彈出元素最須要的元素。
當你在PriorityQueue上調用offer()方法來插入一個對象時,這個對象會在隊列中被排序。默認排序將使用對象在隊列中天然順序,但你能夠經過提供本身的Comparator來修改這個順序。

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);
        priorityQueue = new PriorityQueue<Integer>(ints);
        QueueDemo.printQ(priorityQueue);

        priorityQueue = new PriorityQueue<Integer>(ints.size(), Collections.reverseOrder());
        priorityQueue.addAll(ints);
        QueueDemo.printQ(priorityQueue);

        String face = "EDUCATION SHOULD ESCHEW";
        List<String> strings = Arrays.asList(face.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> characters = new HashSet<Character>();
        for (char c : face.toCharArray())
            characters.add(c);
        PriorityQueue<Character> characterspc = new PriorityQueue<Character>(characters);
        QueueDemo.printQ(characterspc);
    }

}

在PriorityQueue重複是容許的,最小值擁有最高優先級。

11.12 Collection和Iterator

Collection是描述因此有序列容器的共性的根接口,它可能會被認爲是一個附屬接口,即由於要表示其餘若干接口的共性出現的接口。
經過針對接口而非具體實現編寫代碼,咱們的代碼能夠應用於更多的對象類型。

import java.util.*;

public class InterfaceVsIterator
{
    public static void display(Iterator<Integer> it)
    {
        while (it.hasNext())
        {
            Integer i = it.next();
            System.out.print(i+",");
        }
        System.out.println();
    }
    public static void display(Collection<Integer> ints)
    {
        for(Integer i:ints)
            System.out.print(i+"-");
        System.out.println();
    }
    public static void main(String[] args)
    {
        List<Integer> intList= Arrays.asList(1,2,3,15,3);
        Set<Integer> intSet=new HashSet<Integer>(intList);
        Map<String,Integer> intmap=new LinkedHashMap<String,Integer>();
        String[] s=new String[]{"A","b","c","f","w"};
        for(int i=0;i<s.length;i++)
            intmap.put(s[i],intList.get(i));
        display(intList);
        display(intSet);
        display(intList.iterator());
        System.out.println(intmap);
        System.out.println(intmap.keySet());
        display(intmap.values());
        display(intmap.values().iterator());
    }
}

Collection和Iterator均可以將display()方法與底層容器的特定實現解耦。

import java.util.AbstractCollection;
import java.util.Iterator;

public class CollectionSequence extends AbstractCollection<Integer>
{
    private Integer[] ints=new Integer[]{1,4,2,5,1};
    @Override
    public Iterator<Integer> iterator()
    {
        return new Iterator<Integer>()
        {
            private int index=0;
            @Override
            public boolean hasNext()
            {
                return index<ints.length;
            }

            @Override
            public Integer next()
            {
                return ints[index++];
            }
        };
    }
    @Override
    public int size()
    {
        return ints.length;
    }

    public static void main(String[] args){
        CollectionSequence c=new CollectionSequence();
        InterfaceVsIterator.display(c);
        InterfaceVsIterator.display(c.iterator());
    }
}

生成Iterator是將隊列與消費隊列的方法鏈接在一塊兒耦合度最小的方式,而且與1實現Collection相比,它在序列類上所加的約束少得多。

import java.util.Iterator;

public class NonCollectionSequence extends PetSequence
{
    public Iterator<Integer> iterator()
    {
        return new Iterator<Integer>()
        {
            int index = 0;

            @Override
            public boolean hasNext()
            {
                return index < ints.length;
            }

            @Override
            public Integer next()
            {
                return ints[index++];
            }
        };
    }
    public static void main(String[] args){
        NonCollectionSequence nc=new NonCollectionSequence();
        InterfaceVsIterator.display(nc.iterator());
    }
}

class PetSequence
{
    protected int[] ints = new int[]{1, 4, 2, 5, 3};
}

11.13 Foreach與迭代器

foreach語法主要用於數組,但它也能夠用於任何Collection對象。
Java SE5引入了新的被稱爲Iterable接口,該接口包含一個可以產生Iterator的iterator()方法,而且Iterable接口被foreach用來在序列中移動。所以,若是你建立了一個Iterable的類,均可以用於foreach。

import java.util.Iterator;

public class IterableClass implements Iterable<String>
{
    protected String[] words = ("a,d,fe,r,gd,v,g").split(",");

    @Override
    public Iterator<String> iterator()
    {
        return new Iterator<String>()
        {
            int index = 0;

            @Override
            public boolean hasNext()
            {
                return index < words.length;
            }

            @Override
            public String next()
            {
                return words[index++];
            }
        };
    }
    public static void main(String[] args){
        for(String s:new IterableClass())
            System.out.print(s+",");
    }
}

在Java SE中,大量的類都是Iterable類型,主要包括全部Collection類(不包括各類Map)
foreach能夠用於數組或其餘任何Iterable,可是這並不意味着數組確定也是一個Iterable,二任何自動包裝也不會發生。

import java.util.Arrays;

public class ArrayIsMotIterable
{
    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,2));
        String[] strings={"a","b","c"};
        //test(strings);//轉換失敗
        test(Arrays.asList(strings));
    }
}

嘗試把數組轉換成Iterable會失敗,不存在任何數組到Iterable的自動轉換,必須手工執行這種轉換。

11.13.1 適配器方法慣用法

但願在默認向前的迭代器基礎上,添加產生反向迭代器的能力,所以,不能使用覆蓋,須要添加一個可以產生Iterable對象的方法,該對象能夠用於foreach語句。

mport java.util.Iterator;

public class IterableClass implements Iterable<String>
{
    protected String[] words = ("a,d,fe,r,gd,v,g").split(",");

    @Override
    public Iterator<String> iterator()
    {
        return new Iterator<String>()
        {
            int index =words.length-1 ;

            @Override
            public boolean hasNext()
            {
                return index > -1;
            }

            @Override
            public String next()
            {
                return words[index--];
            }
        };
    }
    public static void main(String[] args){
        for(String s:new IterableClass())
            System.out.print(s+",");
    }

}

若是直接將ral對象置於foreach語句中,將獲得向前迭代器。但若是在對象上調用產生向後迭代器的方法,就會產生不一樣的行爲了。 經過這種方式,能夠添加兩種適配器方法。

相關文章
相關標籤/搜索