java集合框架詳解

java集合框架詳解

這篇文章是複製的春哥的,覺得學的狠好!

轉載請聲明出處:http://blog.csdn.net/zhongkelee/article/details/46801449

綜述

    Java集合就是一個容器。面嚮對象語言對事物的體現都是以對象的形式存在,所以爲了方便對多個對象的操作,就對對象進行存儲,集合就是存儲對象最常用的一種方式。集合只用於存儲對象集合長度是可變的集合可以存儲不同類型的對象。如果往集合裏存放基本數據類型,在存取過程中會有個自動裝箱和拆箱。

    因爲容器中數據結構不同,容器有很多種。不斷地將共性功能向上抽取,形成了集合體系,稱之爲集合框架


    集合框架的頂層就稱之爲Collection接口。所有的集合類都位於java.util包下,查閱API可以得到如下體系結構。在使用一個體系時,原則:參閱頂層內容。建立底層對象


    集合和數組的區別:

  1:數組是固定長度的;集合可變長度的。

  2:數組可以存儲基本數據類型,也可以存儲引用數據類型;集合只能存儲引用數據類型。

  3:數組存儲的元素必須是同一個數據類型;集合存儲的對象可以是不同數據類型。

Collection<E>接口

    Collection:單列集合

         |--List:有序(元素存入集合的順序和取出的順序一致),元素都有索引,允許重複元素。

         |--Set:無序(存入和取出順序有可能不一致),不允許重複元素,必須保證元素的唯一性。

    java.util.Collection接口中的共性方法有:

    1.添加
       boolean add(Object obj):一次添加一個。
       boolean addAll(Collection c):將指定容器中的所有元素添加。

    2.刪除
       void clear():將集合中的元素全刪除,清空集合。
       boolean remove(Object o):刪除集合中指定的對象。注意:刪除成功,集合的長度會改變。
       boolean removeAll(Collection c):刪除部分元素。部分元素和傳入Collection一致。

    3.取交集
       boolean retainAll(Collection c):對當前集合中保留和指定集合中的相同的元素。
       如果兩個集合元素相同,返回false;如果retainAll修改了當前集合,返回true。

    4.獲取長度
       int size():集合中有幾個元素。

    5.判斷
       boolean isEmpty():集合中是否有元素。 
       boolean contains(Object o):集合中是否包含指定元素。
       boolean containsAll(Collection c)集合中是否包含指定的多個元素。

    6.將集合轉成數組
       toArray()
       toArray([])

    下面的代碼就是演示Collection中的基本功能。

[java]  view plain  copy
  1. package ustc.lichunchun.collection.demo;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Collection;  
  5.   
  6. public class CollectionDemo {  
  7.     public static void main(String[] args) {  
  8.         Collection coll = new ArrayList();  
  9.         methodDemo(coll);  
  10.         System.out.println("------------------");  
  11.         methodAllDemo();  
  12.     }  
  13.     /* 
  14.      * 演示Collection中的基本功能。  
  15.      */  
  16.     public static void methodDemo(Collection coll){  
  17.           
  18.         //1.添加元素。  
  19.         coll.add("abc1");  
  20.         coll.add("abc2");  
  21.         coll.add("abc3");  
  22.           
  23.         //2.刪除  
  24.         coll.remove("abc2");//移除和添加元素 --> 會改變集合的長度 --> 集合裏面實際上存的是對象們的引用  
  25.           
  26.         //3.清除。  
  27.         coll.clear();  
  28.           
  29.         //4.判斷包含。  
  30.         System.out.println("contains: "+coll.contains("abc1"));//底層實現判斷用的是equals()  
  31.           
  32.         System.out.println(coll);  
  33.     }  
  34.       
  35.     /* 
  36.      * 演示帶All的方法。 
  37.      */  
  38.     public static void methodAllDemo(){  
  39.           
  40.         //1.創建兩個容器。  
  41.         Collection c1 = new ArrayList();  
  42.         Collection c2 = new ArrayList();  
  43.           
  44.         //2.添加元素。  
  45.         c1.add("abc1");  
  46.         c1.add("abc2");  
  47.         c1.add("abc3");  
  48.         c1.add("abc4");  
  49.           
  50.         c2.add("abc2");  
  51.         c2.add("abc3");  
  52.         c2.add("abc5");  
  53.           
  54.         //往c1中添加c2。  
  55.         c1.addAll(c2);  
  56.           
  57.         //判斷c1中是否包含c2中的所有元素。  
  58.         boolean b = c1.containsAll(c2);  
  59.         System.out.println("b = "+b);  
  60.           
  61.         //從c1中刪除c2。將c1中和c2相同的元素從c1中刪除。  
  62.         c1.removeAll(c2);  
  63.           
  64.         //將c1中和c2不同的元素從c1中刪除。保留c1中和c2相同的元素。  
  65.         c1.retainAll(c2);  
  66.         System.out.println(c1);  
  67.     }  
  68. }  

    疑問:Collection 接口中明明沒有toString()聲明,怎麼可能有權利調用這個ArrayList類的"特有"方法? (雖然ArrayList類繼承它父類有toString()複寫的方法了,但這個是ArrayList子類特有的方法啊,不符合多態的解釋呀?)

    下面這段解釋截取自Google找到的答案

[java]  view plain  copy
  1.  樓主懂得思考,先表揚一下。下面將引用一段接口的說明,你可以看看:  
  2. 9.2 Interface Members  
  3. The members of an interface are:Those members declared in the interface.   
  4. Those members inherited from direct superinterfaces.   
  5. If an interface has no direct superinterfaces, then the interface implicitly declares a public abstract member method m with signature s, return type r, and throws clause t corresponding to each public instance method m with signature s, return type r, and throws clause t declared in Object, unless a method with the same signature, same return type, and a compatible throws clause is explicitly declared by the interface. It is a compile-time error if the interface explicitly declares such a method m in the case where m is declared to be final in Object.  
  6.     
  7. 大致意思如下:  
  8. 9.2 接口方法  
  9.   一個接口中的方法有:  
  10.   1).直接聲明在接口中的成員方法;  
  11.   2).直接從父類接口中繼承而來的方法;  
  12.   3).如果一個接口沒有直接的父類接口(也就是其自身就是頂層接口),並且在其沒有顯示聲明相關方法時,那該接口則會根據Object中所有的public的實例方法進行一一映射(比如toString,Hashcode等)。當然如果此接口顯示去聲明一個與Object簽名相同並且帶有final修飾的方法時,則會有編譯期錯誤。  
  13.     
  14. 所以:由超類聲明,子類來new。調用的最終是子類中定義的方法,如果子類沒有,則調用子類的父類方法。這存在一種向上追溯的過程。說明是完全正確的。  
  15.     
  16. 完全贊同!  
  17. 這裏做一點補充。  
  18. 根據這一條說明,在List list=new ArrayList()之後,在list當中將可以調用object當中所有聲明public的方法,而調用的方法實體是來自ArrayList的。而之所以沒有list不可以調用,clone()與finalize()方法,只是因爲它們是protected的。  
  19. 學習了。  
  20. 不過很好奇,33樓的大哥,這條如此原版的聲明出自哪個參考書籍呢?學java就該看這種資料啊。  

    7.取出集合元素。
        Iterator iterator():獲取集合中元素上迭代功能的迭代器對象。

Iterator<E>接口

    java.util.Iterator接口是一個對 collection 進行迭代的迭代器,作用是取出集合中的元素

    Iterator iterator():獲取集合中元素上迭代功能的迭代器對象。

    迭代:取出元素的一種方式。有沒有啊?有!取一個。還有沒有啊?有!取一個。還有沒有啊?沒有。算了。

    迭代器:具備着迭代功能的對象。迭代器對象不需要new。直接通過 iterator()方法獲取即可。

    迭代器是取出Collection集合中元素的公共方法。


    每一個集合都有自己的數據結構,都有特定的取出自己內部元素的方式。爲了便於操作所有的容器,取出元素,將容器內部的取出方式按照一個統一的規則向外提供,這個規則就是Iterator接口

    也就說,只要通過該接口就可以取出Collection集合中的元素,至於每一個具體的容器依據自己的數據結構,如何實現的具體取出細節,這個不用關心,這樣就降低了取出元素和具體集合的耦合性

    Iterator it = coll.iterator();//獲取容器中的迭代器對象,至於這個對象是是什麼不重要。這對象肯定符合一個規則Iterator接口。

[java]  view plain  copy
  1. package ustc.lichunchun.collection.demo;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Collection;  
  5. import java.util.Iterator;  
  6.   
  7. public class IteratorDemo {  
  8.   
  9.     public static void main(String[] args) {  
  10.           
  11.         //1.創建集合。  
  12.         Collection coll = new ArrayList();  
  13.           
  14.         coll.add("abc1");  
  15.         coll.add("abc2");  
  16.         coll.add("abc3");  
  17.           
  18.         //方式一:獲取該容器的迭代器。  
  19.         Iterator it = coll.iterator();  
  20.         while(it.hasNext()){  
  21.             System.out.println(it.next());  
  22.         }  
  23.           
  24.         //方式二:直接for+alt+/,選擇第三個。  
  25.         for (Iterator it = coll.iterator(); it.hasNext();) {  
  26.             System.out.println(it.next());  
  27.         }  
  28.           
  29.         System.out.println(it.next());//abc1  
  30.         System.out.println(it.next());//abc2  
  31.         System.out.println(it.next());//abc3  
  32.         System.out.println(it.next());//java.util.NoSuchElementException   
  33.     }  
  34. }  
      爲了降低容器的數據結構和取出容器元素的方法之間的耦合性,把訪問、取出容器元素的容器的內部類進行共性抽取,即各種容器的相應內部類都實現了Iterator接口,實現了hasNext()、next()、remove()方法。例如如下截取自ArrayList類的iterator()方法的底層實現代碼
[java]  view plain  copy
  1. public Iterator<E> iterator() {  
  2.         return new Itr();//取出ArrayList容器中元素的迭代器功能,返回的是一個Itr()迭代器對象,也就是實現Iterator接口的內部類對象。  
  3.     }  
  4.   
  5.     /** 
  6.      * An optimized version of AbstractList.Itr 
  7.      */  
  8.     private class Itr implements Iterator<E> {//-->ArrayList容器的內部類,實現了Iterator迭代接口(迭代器),裏面有hasNext()、next()、remove()方法。  
  9.         int cursor;       // index of next element to return  
  10.         int lastRet = -1// index of last element returned; -1 if no such  
  11.         int expectedModCount = modCount;  
  12.   
  13.         public boolean hasNext() {  
  14.             return cursor != size;  
  15.         }  
  16.       
  17.         @SuppressWarnings("unchecked")  
  18.         public E next() {  
  19.             checkForComodification();  
  20.             int i = cursor;  
  21.             if (i >= size)  
  22.                 throw new NoSuchElementException();  
  23.             Object[] elementData = ArrayList.this.elementData;  
  24.             if (i >= elementData.length)  
  25.                 throw new ConcurrentModificationException();  
  26.             cursor = i + 1;  
  27.             return (E) elementData[lastRet = i];  
  28.         }  
  29.   
  30.         public void remove() {  
  31.             if (lastRet < 0)  
  32.                 throw new IllegalStateException();  
  33.             checkForComodification();  
  34.   
  35.             try {  
  36.                 ArrayList.this.remove(lastRet);  
  37.                 cursor = lastRet;  
  38.                 lastRet = -1;  
  39.                 expectedModCount = modCount;  
  40.             } catch (IndexOutOfBoundsException ex) {  
  41.                 throw new ConcurrentModificationException();  
  42.             }  
  43.         }  
  44.   
  45.         @Override  
  46.         @SuppressWarnings("unchecked")  
  47.         public void forEachRemaining(Consumer<? super E> consumer) {  
  48.             Objects.requireNonNull(consumer);  
  49.             final int size = ArrayList.this.size;  
  50.             int i = cursor;  
  51.             if (i >= size) {  
  52.                 return;  
  53.             }  
  54.             final Object[] elementData = ArrayList.this.elementData;  
  55.             if (i >= elementData.length) {  
  56.                 throw new ConcurrentModificationException();  
  57.             }  
  58.             while (i != size && modCount == expectedModCount) {  
  59.                 consumer.accept((E) elementData[i++]);  
  60.             }  
  61.             // update once at end of iteration to reduce heap write traffic  
  62.             cursor = i;  
  63.             lastRet = i - 1;  
  64.             checkForComodification();  
  65.         }  
  66.   
  67.         final void checkForComodification() {  
  68.             if (modCount != expectedModCount)  
  69.                 throw new ConcurrentModificationException();  
  70.         }  
  71.     }  
List<E>接口

   List本身是Collection接口的子接口,具備了Collection的所有方法。List集合的具體子類:子類之所以區分是因爲內部的數據結構(存儲數據的方式)不同。

   List:有序(元素存入集合順序和取出一致),元素都有索引,允許重複元素-->自定義元素類型都要複寫equals方法
        |--Vector:底層的數據結構是數組。數組是可變長度的。線程同步的。增刪和查詢都巨慢!
        |--ArrayList:底層的也是數組結構,也是長度可變的。線程不同步的,替代了Vector。增刪速度不快。查詢速度很快。(因爲在內存中是連續空間)
        |--LinkedList:底層的數據結構是鏈表,線程不同步的。增刪速度很快。查詢速度較慢。(因爲在內存中需要一個個查詢、判斷地址來尋找下一元素)

    可變長度數組的原理
    不斷new新數組並將原數組元素複製到新數組。即當元素超出數組長度,會產生一個新數組,將原數組的數據複製到新數組中,再將新的元素添加到新數組中。

    ArrayList:是按照原數組的50%延長。構造一個初始容量爲 10 的空列表。

    Vector:是按照原數組的100%延長。

    首先學習List體系特有的共性方法,查閱方法發現List的特有方法都有索引(角標),這是該集合最大的特點。也就是說,List的特有方法都是圍繞索引(角標)定義的。

    List集合支持對元素的增、刪、改、查

    1.添加(增):
        add(index, element):在指定的索引位插入元素。
        addAll(index, collection):在指定的索引位插入一堆元素。

    2.刪除(刪):
        remove(index):刪除指定索引位的元素。 返回被刪的元素。

    3.獲取(查):
        element get(index):通過索引獲取指定元素。
        int indexOf(element):獲取指定元素第一次出現的索引位,如果該元素不存在返回—1;所以,通過—1,可以判斷一個元素是否存在。
        int lastIndexOf(element) :反向索引指定元素的位置。
        List subList(start,end) :獲取子列表。

    4.修改(改):
        element set(index, newElement):對指定索引位進行元素的修改。

    下面的代碼演示了List的特有方法:

[java]  view plain  copy
  1. package ustc.lichunchun.list.demo;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Iterator;  
  5. import java.util.List;  
  6.   
  7. public class ListDemo {  
  8.   
  9.     public static void main(String[] args) {  
  10.         List list = new ArrayList();  
  11.         methodDemo(list);  
  12.     }  
  13.     /* 
  14.      * 演示List特有的方法。 
  15.      */  
  16.     public static void methodDemo(List list){  
  17.         //1.常規添加元素。  
  18.         list.add("abc1");  
  19.         list.add("abc2");  
  20.         list.add("abc3");  
  21.           
  22.         //2.插入元素。  
  23.         list.add(1,"hehe");  
  24.           
  25.         //3.刪除。  
  26.         list.remove(1);  
  27.         list.remove(1);  
  28.           
  29.         //4.獲取。  
  30.         System.out.println(list.get(3));// java.lang.IndexOutOfBoundsException  
  31.         System.out.println(list.get(1));  
  32.         System.out.println(list.indexOf("abc3"));  
  33.           
  34.         //5.修改。  
  35.         list.set(1,"keke");  
  36.           
  37.         System.out.println(list);  
  38.           
  39.         //6.取出集合中所有的元素。  
  40.         for (Iterator it = list.iterator(); it.hasNext();) {  
  41.             System.out.println("iterator: "+it.next());  
  42.         }  
  43.           
  44.         //7.List集合特有的取出方式。遍歷。  
  45.         for (int i = 0; i < list.size(); i++) {  
  46.             System.out.println("get: "+list.get(i));  
  47.         }  
  48.     }  
  49. }  
     5.獲取所有元素:
    ListIterator listIterator():list集合特有的迭代器。

    在進行list列表元素迭代的時候,如果想要在迭代過程中,想要對元素進行操作的時候,比如滿足條件添加新元素。會發生ConcurrentModificationException併發修改異常。

    導致的原因是:集合引用和迭代器引用在同時操作元素,通過集合獲取到對應的迭代器後,在迭代中,進行集合引用的元素添加,迭代器並不知道,所以會出現異常情況。

    如何解決呢?既然是在迭代中對元素進行操作,找迭代器的方法最爲合適。可是Iterator中只有hasNext,next,remove方法。通過查閱的它的子接口,ListIterator,發現該列表迭代器接口具備了對元素的增、刪、改、查的動作。

    ListIterator是List集合特有的迭代器。

    ListIterator it = list.listIterator; //取代Iterator it = list.iterator;


[java]  view plain  copy
  1. package ustc.lichunchun.list.demo;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5. import java.util.ListIterator;  
  6.   
  7. public class ListIteratorDemo {  
  8.   
  9.     public static void main(String[] args) {  
  10.           
  11.         List list = new ArrayList();  
  12.           
  13.         list.add("abc1");  
  14.         list.add("abc2");  
  15.         list.add("abc3");  
  16.         list.add("abc4");  
  17.           
  18.         //需求:在遍歷的過程中,如果遍歷到abc2,添加一個元素haha  
  19.         for (Iterator it = list.iterator(); it.hasNext();) {  
  20.             Object obj = it.next();//java.util.ConcurrentModificationException   
  21.             if(obj.equals("abc2")){  
  22.                 list.add("haha");  
  23.             }  
  24.         }  
  25.         //上述代碼出現的問題:  
  26.         //迭代器it在操作容器元素,迭代過程中使用了集合對象list同時對元素進行操作。  
  27.         //產生迭代結果的不確定性,引發了併發修改異常。  
  28.         //解決思想:在迭代過程中,想要執行一些操作,使用迭代器的方法就可以了。  
  29.   
  30.         //使用List集合特有的迭代器:ListIterator,通過List集合的方法listIterator()獲取該列表迭代器對象。  
  31.         //ListIterator可以實現在迭代過程中的增刪改查,還可以逆向遍歷。(底層使用了List集合的角標)  
  32.         //總結:在迭代過程中想要對列表List元素進行操作的時候,就要使用列表迭代器ListIterator.  
  33.           
  34.         for (ListIterator it = list.listIterator(); it.hasNext();) {  
  35.             Object obj = it.next();  
  36.             if(obj.equals("abc2")){  
  37.                 it.add("haha");  
  38.             }  
  39.         }  
  40.         System.out.println(list);//[abc1, abc2, haha, abc3, abc4]  
  41.     }  
  42. }  
ArrayList<E>類

    接下來先討論List接口的第一個重要子類:java.util.ArrayList<E>類,我這裏先拋開泛型不說,本篇後面有專門闡述。但要注意,由於還沒有使用泛型,利用Iterator的next()方法取出的元素必須向下轉型,纔可使用子類特有方法。針對ArrayList類,我們最需要注意的是,ArrayList的contains方法底層使用的equals方法判別的,所以自定義元素類型中必須複寫Object的equals方法

    針對這個問題,我們來講幾個小練習。

    練習1: 往ArrayList中存儲自定義對象。Person(name, age)

    思路:
    1.描述Person。
    2.定義容器對象。
    3.將多個Person對象,存儲到集合中。
    4.取出Person對象。-->注意自定義對象複寫toString方法,直接打印p纔有意義。

[java]  view plain  copy
  1. package ustc.lichunchun.list.test;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Iterator;  
  5. import java.util.List;  
  6.   
  7. import ustc.lichunchun.domian.Person;  
  8.   
  9. public class ArrayListTest {  
  10.   
  11.     public static void main(String[] args) {  
  12.           
  13.         //1.創建ArrayList集合對象。  
  14.         List list = new ArrayList();  
  15.           
  16.         //2.添加Person類型的對象。  
  17.         Person p1 = new Person("lisi1"21);  
  18.         Person p2 = new Person("lisi2"22);  
  19.           
  20.         list.add(p1);//add(Object obj)  
  21.         list.add(p2);  
  22.         list.add(new Person("lisi3"23));  
  23.           
  24.         //3.取出元素。  
  25.         for (Iterator it = list.iterator(); it.hasNext();) {  
  26.             //it.next():取出的元素都是Object類型的。需要用到具體對象內容時,需要向下轉型。  
  27.             Person p = (Person)it.next();  
  28.             System.out.println(p.getName()+":"+p.getAge());//如果不向下轉型,Object類對象沒有getName、getAge方法。  
  29.         }  
  30.     }  
  31. }  
     練習2:定義功能,去除ArrayList集合中的重複元素。

    思路:
    1.最後唯一性的元素也很多,可以先定義一個容器用於存儲這些唯一性的元素。
    2.對原有容器進行元素的獲取,併到臨時容器中去判斷是否存在。容器本身就有這功能,判斷元素是否存在。
        -->contains()底層原理就是使用的equals(),而且這裏用的是String類複寫的的equals
    3.存在就不存儲,不存在就存儲。
    4.遍歷完原容器後,臨時容器中存儲的就是唯一性的元素。

[java]  view plain  copy
  1. package ustc.lichunchun.list.test;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Iterator;  
  5. import java.util.List;  
  6. import java.util.ListIterator;  
  7.   
  8. public class ArrayListTest2 {  
  9.   
  10.     public static void main(String[] args) {  
  11.         /* 
  12.          * 練習2:定義功能,去除ArrayList集合中的重複元素。 
  13.          */  
  14.         List list = new ArrayList();  
  15.         list.add("abc1");  
  16.         list.add("abc4");  
  17.         list.add("abc2");  
  18.         list.add("abc1");  
  19.         list.add("abc4");  
  20.         list.add("abc4");  
  21.         list.add("abc2");  
  22.         list.add("abc1");  
  23.         list.add("abc4");  
  24.         list.add("abc2");  
  25.           
  26.         System.out.println(list);//[abc1, abc4, abc2, abc1, abc4, abc4, abc2, abc1, abc4, abc2]  
  27.         singleElement2(list);  
  28.         System.out.println(list);//[abc1, abc4, abc2]  
  29.     }  
  30.       
  31.     /* 
  32.      * 取出重複元素方式一。 
  33.      * 定義功能,取出重複元素。因爲List帶有角標,比較容易進行for循環。 
  34.      */  
  35.     public static void singleElement(List list){  
  36.         for (int x = 0; x < list.size()-1; x++){  
  37.             Object obj = list.get(x);  
  38.             for(int y = x+1; y < list.size(); y++){  
  39.                 if (obj.equals(list.get(y))){  
  40.                     list.remove(y--);//記住:remove、add等方法,會改變原數組長度!注意角標變化。  
  41.                 }  
  42.             }  
  43.         }  
  44.     }  
  45.       
  46.     /* 
  47.      * 取出重複元素方式二。 
  48.      * 思路: 
  49.      * 1.最後唯一性的元素也很多,可以先定義一個容器用於存儲這些唯一性的元素。 
  50.      * 2.對原有容器進行元素的獲取,併到臨時容器中去判斷是否存在。容器本身就有這功能,判斷元素是否存在。 
  51.      * 3.存在就不存儲,不存在就存儲。 
  52.      * 4.遍歷完原容器後,臨時容器中存儲的就是唯一性的元素。 
  53.      */  
  54.     public static void singleElement2(List list){  
  55.         //1.定義一個臨時容器  
  56.         List temp = new ArrayList();  
  57.           
  58.         //2.遍歷原容器  
  59.         for (Iterator it = list.iterator(); it.hasNext();) {  
  60.             Object obj = (Object) it.next();  
  61.               
  62.             //3.在臨時容器中判斷遍歷到的元素是否存在  
  63.             if(!temp.contains(obj))//contains()底層原理就是使用的equals(),而且這裏用的是String類複寫的equals。  
  64.                 //如果不存在,就存儲到臨時容器中  
  65.                 temp.add(obj);  
  66.         }  
  67.         //將原容器清空  
  68.         list.clear();  
  69.         //將臨時容器中的元素都存儲到原容器中  
  70.         list.addAll(temp);  
  71.     }  
  72. }  
     練習3:ArrayList取出重複的自定義元素。

    記住:往集合裏面存儲自定義元素,該元素所屬類一定要覆蓋equals、toString方法!

[java]  view plain  copy
  1. package ustc.lichunchun.domian;  
  2.   
  3. public class Person{  
  4.     private String name;  
  5.     private int age;  
  6.     public Person() {  
  7.         super();  
  8.     }  
  9.     public Person(String name, int age) {  
  10.         super();  
  11.         this.name = name;  
  12.         this.age = age;  
  13.     }  
  14.     public String getName() {  
  15.         return name;  
  16.     }  
  17.     public void setName(String name) {  
  18.         this.name = name;  
  19.     }  
  20.     public int getAge() {  
  21.         return age;  
  22.     }  
  23.     public void setAge(int age) {  
  24.         this.age = age;  
  25.     }  
  26.     @Override  
  27.     public String toString() {  
  28.         return "Person [name=" + name + ", age=" + age + "]";  
  29.     }  
  30.     /* 
  31.      * 建立Person類自己的判斷對象是否相同的依據,必須要覆蓋Object類中的equals方法。 
  32.      */  
  33.     public boolean equals(Object obj) {  
  34.         //爲了提高效率,如果比較的對象是同一個,直接返回true即可。  
  35.         if(this == obj)  
  36.             return true;  
  37.           
  38.         if(!(obj instanceof Person))  
  39.                 throw new ClassCastException("類型錯誤");  
  40.         Person p = (Person)obj;  
  41.           
  42.         return this.name.equals(p.name) && this.age==p.age;  
  43.     }  
  44.     /*@Override 
  45.     public boolean equals(Object obj) { 
  46.         if (this == obj) 
  47.             return true; 
  48.         if (obj == null) 
  49.             return false; 
  50.         if (getClass() != obj.getClass()) 
  51.             return false; 
  52.         Person other = (Person) obj; 
  53.         if (age != other.age) 
  54.             return false; 
  55.         if (name == null) { 
  56.             if (other.name != null) 
  57.                 return false; 
  58.         } else if (!name.equals(other.name)) 
  59.             return false; 
  60.         return true; 
  61.     }*/  
  62. }  

     contains()方法底層調用的是容器中元素對象的equals()方法!這裏如果Person類自身不定義equals方法,就使用Object的equals方法,比較的就僅僅是地址了。

[java]  view plain  copy
  1. package ustc.lichunchun.list.test;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Iterator;  
  5. import java.util.List;  
  6.   
  7. import ustc.lichunchun.domian.Person;  
  8.   
  9. public class ArrayListTest3 {  
  10.   
  11.     public static void main(String[] args) {  
  12.         /* 
  13.          * 練習3:ArrayList取出重複的自定義元素。 
  14.          *  
  15.          * 記住:往集合裏面存儲自定義元素,該元素所屬類一定要覆蓋equals、toString方法! 
  16.          */  
  17.         List list = new ArrayList();  
  18.         Person p = new Person("li",19);  
  19.         list.add(p);  
  20.         list.add(p);//存儲了一個地址相同的對象。在equals方法中直接先this==obj即可。  
  21.         list.add(new Person("li",20));  
  22.         list.add(new Person("li",23));  
  23.         list.add(new Person("li",26));  
  24.         list.add(new Person("li",23));  
  25.         list.add(new Person("li",26));  
  26.         list.add(new Person("li",20));  
  27.           
  28.         System.out.println(list);  
  29.         singleElement(list);  
  30.         System.out.println(list);  
  31.     }  
  32.     public static void singleElement(List list){  
  33.         List temp = new ArrayList();  
  34.         for (Iterator it = list.iterator(); it.hasNext();) {  
  35.             Object obj = (Object) it.next();  
  36.             if(!temp.contains(obj))// --> contains()方法底層調用的是容器中元素對象的equals()方法!  
  37.                 //這裏如果Person類自身不定義equals方法,就使用Object的equals方法,比較的就僅僅是地址了。  
  38.                 temp.add(obj);  
  39.         }  
  40.         list.clear();  
  41.         list.addAll(temp);  
  42.     }  
  43. }  
LinkedList<E>類

    java.util.LinkedList<E>類是List接口的鏈表實現,可以利用LinkedList實現堆棧、隊列結構。它的特有方法有如下這些:    

    addFirst();
    addLast();
    在jdk1.6以後:
    offerFirst();
    offerLast();

    getFirst():獲取鏈表中的第一個元素。如果鏈表爲空,拋出NoSuchElementException;
    getLast();
    在jdk1.6以後:
    peekFirst();獲取鏈表中的第一個元素。如果鏈表爲空,返回null。
    peekLast();

    removeFirst():獲取鏈表中的第一個元素,但是會刪除鏈表中的第一個元素。如果鏈表爲空,拋出NoSuchElementException
    removeLast();
    在jdk1.6以後:
    pollFirst();獲取鏈表中的第一個元素,但是會刪除鏈表中的第一個元素。如果鏈表爲空,返回null。
    pollLast();

[java]  view plain  copy
  1. package ustc.lichunchun.list.linkedlist;  
  2.   
  3. import java.util.LinkedList;  
  4.   
  5. public class LinkedListDemo {  
  6.   
  7.     public static void main(String[] args) {  
  8.         //1.創建一個鏈表對象。  
  9.         LinkedList link = new LinkedList();  
  10.           
  11.         //演示xxxFirst()、xxxLast()方法。  
  12.           
  13.         //2.添加方法。  
  14.         link.addFirst("abc1");  
  15.         link.addFirst("abc2");  
  16.         link.addFirst("abc3");  
  17.           
  18.         //3.獲取元素。  
  19.         System.out.println(link.getFirst());//abc3  
  20.         System.out.println(link.getFirst());//abc3  
  21.           
  22.         //4.刪除元素。  
  23.         System.out.println(link.removeFirst());//abc3  
  24.         System.out.println(link.removeFirst());//abc2  
  25.           
  26.         //5.取出link中所有元素。  
  27.         while(!link.isEmpty())  
  28.             System.out.println(link.removeLast());//removeFirst()  
  29.         link.contains("abc3");//false  
  30.     }  
  31. }  
     練習:請通過LInkedList實現一個堆棧,或者隊列數據結構。

    堆棧:先進後出。First In Last Out FILO。

    隊列:先進先出。First In First Out   FIFO。

    隊列結構代碼如下:

[java]  view plain  copy
  1. package ustc.lichunchun.list.linkedlist;  
  2.   
  3. import java.util.LinkedList;  
  4.   
  5. /* 
  6.  * 描述一個隊列數據結構。內部使用的是LinkedList。 
  7.  */  
  8. public class MyQueue {  
  9.     private LinkedList link;  
  10.   
  11.     MyQueue() {  
  12.         link = new LinkedList();  
  13.     }  
  14.   
  15.     /** 
  16.      * 添加元素的方法。 
  17.      */  
  18.     public void myAdd(Object obj) {  
  19.         // 內部使用的是LinkedList的方法。  
  20.         link.addFirst(obj);  
  21.     }  
  22.   
  23.     /** 
  24.      * 獲取隊列元素的方法。 
  25.      */  
  26.     public Object myGet() {  
  27.         return link.removeLast();  
  28.     }  
  29.   
  30.     /** 
  31.      * 集合中是否有元素的方法。 
  32.      */  
  33.     public boolean isNull() {  
  34.         return link.isEmpty();  
  35.     }  
  36. }  

    堆棧結構代碼如下:

[java]  view plain  copy
  1. package ustc.lichunchun.list.linkedlist;  
  2.   
  3. import java.util.LinkedList;  
  4.   
  5. /* 
  6.  *  實現一個堆棧結構。內部使用的是LinkedList。 
  7.  */  
  8. public class MyStack {  
  9.     private LinkedList link;  
  10.   
  11.     MyStack() {  
  12.         link = new LinkedList();  
  13.     }  
  14.   
  15.     public void myAdd(Object obj) {  
  16.         link.addFirst(obj);  
  17.     }  
  18.   
  19.     public Object myGet() {  
  20.         return link.removeFirst();  
  21.     }  
  22.   
  23.     public boolean isNull() {  
  24.         return link.isEmpty();  
  25.     }  
  26. }  

    測試:

[java]  view plain  copy
  1. package ustc.lichunchun.list.linkedlist;  
  2.   
  3. import java.util.LinkedList;  
  4.   
  5. public class LinkedListTest {  
  6.   
  7.     public static void main(String[] args) {  
  8.         /* 
  9.          * 練習:請通過LInkedList實現一個堆棧,或者隊列數據結構。 
  10.          * 堆棧:先進後出。First In Last Out      FILO. 
  11.          * 隊列:先進先出。First In First Out  FIFO. 
  12.          */  
  13.           
  14.         //1.創建自定義的隊列對象。  
  15.         MyQueue queue = new MyQueue();  
  16.           
  17.         //2.添加元素。  
  18.         queue.myAdd("abc1");  
  19.         queue.myAdd("abc2");  
  20.         queue.myAdd("abc3");  
  21.         queue.myAdd("abc4");  
  22.           
  23.         //3.獲取所有元素。先進先出。  
  24.         while(!queue.isNull())  
  25.             System.out.println(queue.myGet());  
  26.         System.out.println("--------------------------");  
  27.           
  28.         //1.創建自定義的堆棧對象。  
  29.         MyStack stack = new MyStack();  
  30.           
  31.         //2.添加元素。  
  32.         stack.myAdd("def5");  
  33.         stack.myAdd("def6");  
  34.         stack.myAdd("def7");  
  35.         stack.myAdd("def8");  
  36.           
  37.         //3.獲取所有元素。先進後出。  
  38.         while(!stack.isNull())  
  39.             System.out.println(stack.myGet());  
  40.     }  
  41. }  

練習1:帶猜數字遊戲的用戶登錄註冊案例--集合版

需求分析:

[java]  view plain  copy
  1. 需求:用戶登錄註冊案例。  
  2.   
  3. 按照如下的操作,可以讓我們更符號面向對象思想  
  4.     A:有哪些類呢?  
  5.     B:每個類有哪些東西呢?  
  6.     C:類與類之間的關係是什麼呢?  
  7.       
  8. 分析:  
  9.     A:有哪些類呢?  
  10.         用戶類  
  11.         測試類  
  12.     B:每個類有哪些東西呢?  
  13.         用戶類:  
  14.             成員變量:用戶名,密碼  
  15.             構造方法:無參構造  
  16.             成員方法:getXxx()/setXxx()  
  17.                        登錄,註冊  
  18.                          
  19.             假如用戶類的內容比較對,將來維護起來就比較麻煩,爲了更清晰的分類,我們就把用戶又劃分成了兩類  
  20.                 用戶基本描述類  
  21.                     成員變量:用戶名,密碼  
  22.                     構造方法:無參構造  
  23.                     成員方法:getXxx()/setXxx()  
  24.                 用戶操作類  
  25.                     登錄,註冊  
  26.         測試類:  
  27.             main方法。  
  28.     C:類與類之間的關係是什麼呢?  
  29.         在測試類中創建用戶操作類和用戶基本描述類的對象,並使用其功能。  
  30.           
  31. 分包:  
  32.     A:功能劃分  
  33.     B:模塊劃分  
  34.     C:先按模塊劃分,再按功能劃分  
  35.       
  36. 今天我們選擇按照功能劃分:  
  37.     用戶基本描述類包 ustc.lichunchun.pojo  
  38.     用戶操作接口 ustc.lichunchun.dao  
  39.     用戶操作類包 ustc.lichunchun.dao.impl  
  40.         本文中是集合實現,後續會有IO實現、GUI實現和數據庫實現。  
  41.     用戶測試類 ustc.lichunchun.test  
代碼如下:

[java]  view plain  copy
  1. package ustc.lichunchun.pojo;  
  2. /** 
  3.  * 這是用戶基本描述類 
  4.  *  
  5.  * @author 李春春 
  6.  * @version V1.0 
  7.  * 
  8.  */  
  9. public class User {  
  10.     // 用戶名  
  11.     private String username;  
  12.     // 密碼  
  13.     private String password;  
  14.   
  15.     public User() {  
  16.         super();  
  17.     }  
  18.   
  19.     public String getUsername() {  
  20.         return username;  
  21.     }  
  22.   
  23.     public void setUsername(String username) {  
  24.         this.username = username;  
  25.     }  
  26.   
  27.     public String getPassword() {  
  28.         return password;  
  29.     }  
  30.   
  31.     public void setPassword(String password) {  
  32.         this.password = password;  
  33.     }  
  34. }  
[java]  view plain  copy
  1. package ustc.lichunchun.dao;  
  2.   
  3. import ustc.lichunchun.pojo.User;  
  4. /** 
  5.  * 這時針對用戶進行操作的接口 
  6.  *  
  7.  * @author 李春春 
  8.  * @version V1.0 
  9.  * 
  10.  */  
  11. public interface UserDao {  
  12.     /** 
  13.      * 這是用戶登錄功能 
  14.      *  
  15.      * @param username 
  16.      *            用戶名 
  17.      * @param password 
  18.      *            密碼 
  19.      * @return 返回登陸是否成功 
  20.      */  
  21.     public abstract boolean isLogin(String username, String password);  
  22.   
  23.     /** 
  24.      * 這是用戶註冊功能 
  25.      *  
  26.      * @param user 
  27.      *            要註冊的用戶信息 
  28.      */  
  29.     public abstract void regist(User user);  
  30. }  
[java]  view plain  copy
  1. package ustc.lichunchun.dao.impl;  
  2.   
  3. import java.util.ArrayList;  
  4.   
  5. import ustc.lichunchun.dao.UserDao;  
  6. import ustc.lichunchun.pojo.User;  
  7. /** 
  8.  * 這是用戶操作的具體實現類(集合版) 
  9.  *  
  10.  * @author 李春春 
  11.  * @version V1.0 
  12.  * 
  13.  */  
  14. public class UserDaoImpl implements UserDao {  
  15.     //爲了讓多個方法能夠使用同一個集合,就把集合定義爲成員變量。  
  16.     //爲了不讓外人看到,用private  
  17.     //爲了讓多個對象共享同一個成員變量,用static  
  18.     private static ArrayList<User> array = new ArrayList<User>();  
  19.     @Override  
  20.     public boolean isLogin(String username, String password) {  
  21.         //遍歷集合,獲取每一個用戶,並判斷用戶的用戶名和密碼是否和傳遞過來的匹配  
  22.         boolean flag = false;  
  23.           
  24.         for(User u : array){  
  25.             if(u.getUsername().equalsIgnoreCase(username) && u.getPassword().equalsIgnoreCase(password)){  
  26.                 flag = true;  
  27.                 break;  
  28.             }  
  29.         }  
  30.           
  31.         return flag;  
  32.     }  
  33.   
  34.     @Override  
  35.     public void regist(User user) {  
  36.         //把用戶信息存入集合  
  37.         array.add(user);  
  38.     }  
  39. }  
[java]  view plain  copy
  1. package ustc.lichunchun.test;  
  2.   
  3. import java.util.Scanner;  
  4.   
  5. import ustc.lichunchun.dao.UserDao;  
  6. import ustc.lichunchun.dao.impl.UserDaoImpl;  
  7. import ustc.lichunchun.game.GuessNumber;  
  8. import ustc.lichunchun.pojo.User;  
  9.   
  10. /** 
  11.  * 用戶測試類 
  12.  *  
  13.  * @author 李春春 
  14.  * @version V1.0 
  15.  *  
  16.  * 新增加了兩個小問題: 
  17.  * A.多個對象共享同一個成員變量,用靜態 
  18.  * B.循環裏面如果有switch,並且在switch裏面有break,那麼結束的不是循環,而是switch語句 
  19.  * 
  20.  */  
  21. public class UserTest {  
  22.   
  23.     public static void main(String[] args) {  
  24.         //爲了能夠回來  
  25.         while (true) {  
  26.             // 歡迎界面,給出選擇項  
  27.             System.out.println("--------------歡迎光臨--------------");  
  28.             System.out.println("1 登陸");  
  29.             System.out.println("2 註冊");  
  30.             System.out.println("3 退出");  
  31.             System.out.println("請輸入你的選擇:");  
  32.             //鍵盤錄入選擇,根據選擇做不同的操作  
  33.             Scanner sc = new Scanner(System.in);  
  34.             //爲了後面的錄入信息的方便,所有的數據錄入全部用字符串接收  
  35.             String choiceString = sc.nextLine();  
  36.             //switch語句的多個地方要使用,我就定義到外面  
  37.             UserDao ud = new UserDaoImpl();//多態  
  38.             //經過簡單的思考,我選擇了switch  
  39.             switch (choiceString) {  
  40.             case "1":  
  41.                 //登陸界面,請輸入用戶名和密碼  
  42.                 System.out.println("--------------登錄界面--------------");  
  43.                 System.out.println("請輸入用戶名:");  
  44.                 String username = sc.nextLine();  
  45.                 System.out.println("請輸入密碼:");  
  46.                 String password = sc.nextLine();  
  47.                 //調用登錄功能  
  48.                 boolean flag = ud.isLogin(username, password);  
  49.                 if (flag) {  
  50.                     System.out.println("登陸成功,可以開始玩遊戲了");  
  51.                     System.out.println("你玩麼?y/n");  
  52.                     while(true){  
  53.                         String resultString = sc.nextLine();  
  54.                         if(resultString.equalsIgnoreCase("y")){  
  55.                             GuessNumber.start();  
  56.                             System.out.println("你還玩麼?y/n");  
  57.                         }else{  
  58.                             break;  
  59.                         }  
  60.                     }  
  61.                     System.out.println("謝謝使用,歡迎下次再來");  
  62.                     System.exit(0);  
  63.                     //break;這裏寫break,結束的是switch  
  64.                 } else {  
  65.                     System.out.println("用戶名或者密碼有誤,登錄失敗");  
  66.                 }  
  67.                 break;  
  68.   
  69.             case "2":  
  70.                 //註冊界面,請輸入用戶名和密碼  
  71.                 System.out.println("--------------註冊界面--------------");  
  72.                 System.out.println("請輸入用戶名:");  
  73.                 String newUserName = sc.nextLine();  
  74.                 System.out.println("請輸入密碼:");  
  75.                 String newPassword = sc.nextLine();  
  76.                 //把用戶名和密碼封裝到一個對象中  
  77.                 User user = new User();  
  78.                 user.setUsername(newUserName);  
  79.                 user.setPassword(newPassword);  
  80.                 //調用註冊功能  
  81.                 ud.regist(user);  
  82.                 System.out.println("註冊成功");  
  83.                 break;  
  84.             case "3":  
  85.             default:  
  86.                 System.out.println("謝謝使用,歡迎下次再來");  
  87.                 System.exit(0);  
  88.             }  
  89.         }  
  90.     }  
  91. }  
其中的猜數字遊戲代碼爲:

[java]  view plain  copy
  1. package ustc.lichunchun.game;  
  2.   
  3. import java.util.Scanner;  
  4.   
  5. /** 
  6.  * 這是猜數字小遊戲 
  7.  *  
  8.  * @author 李春春 
  9.  * 
  10.  */  
  11. public class GuessNumber {  
  12.     private GuessNumber() {  
  13.     }  
  14.   
  15.     public static void start() {  
  16.         int num = (int) (Math.random() * 100) + 1;  
  17.         int count = 0;  
  18.         while (true) {  
  19.             System.out.println("請輸入數據(1-100):");  
  20.             Scanner sc = new Scanner(System.in);  
  21.             int guessNum = sc.nextInt();  
  22.             count++;  
  23.             if (guessNum > num) {  
  24.                 System.out.println("你猜的數據" + guessNum + "大了");  
  25.             } else if (guessNum < num) {  
  26.                 System.out.println("你猜的數據" + guessNum + "小了");  
  27.             } else {  
  28.                 System.out.println("恭喜你," + count + "次就猜中了");  
  29.                 break;  
  30.             }  
  31.         }  
  32.     }  
  33. }  
程序運行結果如下:

練習2:控制檯購物管理系統。

這個練習的程序代碼較長,我上傳到資源裏,有興趣的讀者可以下載下來瞅一眼。

詳見:http://download.csdn.net/detail/zhongkelee/8981865

程序運行截圖:

Set<E>接口

     java.util.Set<E>接口,一個不包含重複元素的 collection。更確切地講,set 不包含滿足e1.equals(e2) 的元素對e1 和e2,並且最多包含一個 null 元素。
    Set:不允許重複元素。和Collection的方法相同。Set集合取出方法只有一個:迭代器。
        |--HashSet:底層數據結構是哈希表(散列表)。無序,比數組查詢的效率高。線程不同步的。
                 -->根據哈希衝突的特點,爲了保證哈希表中元素的唯一性,
                      該容器中存儲元素所屬類應該複寫Object類的hashCode、equals方法。
                |--LinkedhashSet有序,HashSet的子類。
        |--TreeSet:底層數據結構是二叉樹。可以對Set集合的元素按照指定規則進行排序。線程不同步的。
                -->add方法新添加元素必須可以同容器已有元素進行比較,
                     所以元素所屬類應該實現Comparable接口的compareTo方法,以完成排序。
             或者添加Comparator比較器,實現compare方法。

    代碼示例:

[java]  view plain  copy
  1. package ustc.lichunchun.set.demo;  
  2.   
  3. import java.util.HashSet;  
  4. import java.util.Iterator;  
  5. import java.util.Set;  
  6.   
  7. public class HashSetDemo {  
  8.   
  9.     public static void main(String[] args) {  
  10.           
  11.         //1.創建一個Set容器對象。  
  12.         Set set = new HashSet();  
  13.           
  14.         //Set set = new LinkedHashSet();如果改成LinkedHashSet,可以實現有序。  
  15.           
  16.         //2.添加元素。  
  17.         set.add("haha");  
  18.         set.add("nba");  
  19.         set.add("abc");  
  20.         set.add("nba");  
  21.         set.add("heihei");  
  22.           
  23.         //3.只能用迭代器取出。  
  24.         for (Iterator it = set.iterator(); it.hasNext();) {  
  25.             System.out.println(it.next());  
  26.         }  
  27.     }  
  28. }  
HashSet<E>類

    java.util.HashSet<E>類實現Set 接口,由哈希表(實際上是一個HashMap 實例)支持。它不保證 set 的迭代順序;特別是它不保證該順序恆久不變。此類允許使用null 元素。

    堆內存的底層實現就是一種哈希表結構,需要通過哈希算法來計算對象在該結構中存儲的地址。這個方法每個對象都具備,叫做hashCode()方法,隸屬於java.lang.Objecct類。hashCode本身調用的是wondows系統本地的算法,也可以自己定義。

    哈希表的原理
    1.對對象元素中的關鍵字(對象中的特有數據),進行哈希算法的運算,並得出一個具體的算法值,這個值稱爲哈希值。
    2.哈希值就是這個元素的位置。
    3.如果哈希值出現衝突,再次判斷這個關鍵字對應的對象是否相同。
       如果對象相同,就不存儲,因爲元素重複。如果對象不同,就存儲,在原來對象的哈希值基礎 +1順延。
    4.存儲哈希值的結構,我們稱爲哈希表。
    5.既然哈希表是根據哈希值存儲的,爲了提高效率,最好保證對象的關鍵字是唯一的。
       這樣可以儘量少的判斷關鍵字對應的對象是否相同,提高了哈希表的操作效率。

    哈希表的特點
    1.不允許存儲重複元素,因爲會發生查找的不確定性。
    2.不保證存入和取出的順序一致,即不保證有序。
    3.比數組查詢的效率高。

    哈希衝突
    當哈希算法算出的兩個元素的值相同時,稱爲哈希衝突。衝突後,需要對元素進行進一步的判斷。判斷的是元素的內容,equals。如果不同,還要繼續計算新的位置,比如地址鏈接法,相當於掛一個鏈表擴展下來。

    如何保證哈希表中元素的唯一性
    元素必須覆蓋hashCode和equals方法。
    覆蓋hashCode方法是爲了根據元素自身的特點確定哈希值。
    覆蓋equals方法,是爲了解決哈希值的衝突。

    如何實現有序
    LinkedHashSet類,可以實現有序。

    廢話不所說,下面我來舉一個例子演示。

    練習:往HashSet中存儲學生對象(姓名,年齡)。同姓名、同年齡視爲同一個人,不存。

    思路:

    1.描述學生。
    2.定義容器。
    3.將學生對象存儲到容器中。

[java]  view plain  copy
  1. package ustc.lichunchun.domian;  
  2.   
  3. public class Student {  
  4.     private String name;  
  5.     private int age;  
  6.     public Student() {  
  7.         super();  
  8.     }  
  9.     public Student(String name, int age) {  
  10.         super();  
  11.         this.name = name;  
  12.         this.age = age;  
  13.     }  
  14.     public String getName() {  
  15.         return name;  
  16.     }  
  17.     public void setName(String name) {  
  18.         this.name = name;  
  19.     }  
  20.     public int getAge() {  
  21.         return age;  
  22.     }  
  23.     public void setAge(int age) {  
  24.         this.age = age;  
  25.     }  
  26.     @Override  
  27.     public String toString() {  
  28.         return "Student [name=" + name + ", age=" + age + "]";  
  29.     }  
  30.     /* 
  31.     @Override 
  32.     public int hashCode() { 
  33.         final int prime = 31; 
  34.         int result = 1; 
  35.         result = prime * result + age; 
  36.         result = prime * result + ((name == null) ? 0 : name.hashCode()); 
  37.         return result; 
  38.     } 
  39.     @Override 
  40.     public boolean equals(Object obj) { 
  41.         if (this == obj) 
  42.             return true; 
  43.         if (obj == null) 
  44.             return false; 
  45.         if (getClass() != obj.getClass()) 
  46.             return false; 
  47.         Student other = (Student) obj; 
  48.         if (age != other.age) 
  49.             return false; 
  50.         if (name == null) { 
  51.             if (other.name != null) 
  52.                 return false; 
  53.         } else if (!name.equals(other.name)) 
  54.             return false; 
  55.         return true; 
  56.     } 
  57.     */  
  58.       
  59.      //覆蓋hashCode方法。根據對象自身的特點定義哈希值。  
  60.     public int hashCode(){  
  61.         final int NUMBER = 31;  
  62.         return name.hashCode()+ age*NUMBER;  
  63.     }  
  64.       
  65.      //需要定義對象自身判斷內容相同的依據。覆蓋equals方法。  
  66.     public boolean equals(Object obj){  
  67.         if (this == obj)  
  68.             return true;  
  69.         if(!(obj instanceof Student))  
  70.             throw new ClassCastException(obj.getClass().getName()+"類型錯誤");  
  71.         Student stu = (Student)obj;  
  72.         return this.name.equals(stu.name) && this.age == stu.age;  
  73.     }  
  74. }  
[java]  view plain  copy
  1. package ustc.lichunchun.set.demo;  
  2.   
  3. import java.util.HashSet;  
  4. import java.util.Iterator;  
  5. import java.util.Set;  
  6.   
  7. import ustc.lichunchun.domian.Student;  
  8.   
  9. public class HashSetTest {  
  10.   
  11.     public static void main(String[] args) {  
  12.         /* 
  13.          * 練習:往HashSet中存儲學生對象(姓名,年齡)。同姓名、同年齡視爲同一個人,不存。 
  14.          * 1.描述學生。 
  15.          * 2.定義容器。 
  16.          * 3.將學生對象存儲到容器中。 
  17.          *  
  18.          * 發現存儲了同姓名、同年齡的學生是可以的。 
  19.          * 原因是每一次存儲學生對象,都先調用hashCode()方法獲取哈希值。 
  20.          * 但此時調用的是Object類中的hashCode。所以同姓名同年齡了,但因爲是不同的對象,哈希值也不同。 
  21.          * 這就是同姓名同年齡存入的原因。 
  22.          *  
  23.          * 解決: 
  24.          * 需要根據學生對象自身的特點來定義哈希值。 
  25.          * 所以就需要覆蓋hashCode方法。 
  26.          *  
  27.          * 發現,當hashCode返回值相同時,會調用equals方法比較兩個對象是否相等。 
  28.          * 還是會出現同姓名同年齡的對象,因爲子類沒有複寫equals方法, 
  29.          * 直接用Object類的equals方法僅僅比較了兩個對象的地址值。 
  30.          * 這就是同姓名同年齡還會存入的原因。 
  31.          *  
  32.          * 解決: 
  33.          * 需要定義對象自身判斷內容相同的依據。 
  34.          * 所以就需要覆蓋equals方法。 
  35.          *  
  36.          * 效率問題: 
  37.          * 儘量減少哈希算法求得的哈希值的衝突。減少equals方法的調用。 
  38.          */  
  39.         //1.創建容器對象。  
  40.         Set set = new HashSet();  
  41.           
  42.         //2.存儲學生對象。  
  43.         set.add(new Student("xiaoqiang",20));  
  44.         set.add(new Student("wangcai",27));  
  45.         set.add(new Student("xiaoming",22));  
  46.         set.add(new Student("xiaoqiang",20));  
  47.         set.add(new Student("daniu",24));  
  48.         set.add(new Student("xiaoming",22));  
  49.           
  50.         //3.獲取所有學生。  
  51.         for (Iterator it = set.iterator(); it.hasNext();) {  
  52.             Student stu = (Student) it.next();  
  53.             System.out.println(stu.getName()+":"+stu.getAge());  
  54.         }  
  55.     }  
  56. }  
     ArrayList存儲元素依賴的是equals方法。比如remove、contains底層判斷用的都是equals方法。
    HashSet判斷元素是否相同:依據的是hashCode和equals方法。如果哈希衝突(哈希值相同),再判斷元素的equals方法。如果equals方法返回true,不存;返回false,存儲!

TreeSet<E>類

    java.util.Set<E>類基於TreeMap的NavigableSet實現。使用元素的自然順序(Comparable的compareTo方法)對元素進行排序,或者根據創建 set 時提供的自定義比較器(Comparator的compare方法)進行排序,具體取決於使用的構造方法。此實現爲基本操作(add、remove和contains)提供受保證的 log(n) 時間開銷。

    TreeSet:可以對元素排序。

    有序:存入和取出的順序一致。--> List

    排序:升序or降序。--> TreeSet

    代碼示例:

[java]  view plain  copy
  1. package ustc.lichunchun.set.demo;  
  2.   
  3. import java.util.Iterator;  
  4. import java.util.Set;  
  5. import java.util.TreeSet;  
  6.   
  7. import ustc.lichunchun.domian.Student;  
  8.   
  9. public class TreeSetDemo {  
  10.   
  11.     public static void main(String[] args) {  
  12.         Set set = new TreeSet();  
  13.           
  14.         set.add("abc");  
  15.         set.add("heihei");  
  16.         set.add("nba");  
  17.         set.add("haha");  
  18.         set.add("heihei");  
  19.           
  20.         for (Iterator it = set.iterator(); it.hasNext();) {  
  21.             System.out.println(it.next());  
  22.         }  
  23.     }  
  24. }  
     程序輸出:


    那如果往TreeSet集合中存入的是自定義元素呢?

    TreeSet排序方式
    需要元素自身具備比較功能。所以元素需要實現Comparable接口覆蓋compareTo方法。如果元素不具備比較性,在運行時會發生ClassCastException異常。

    TreeSet能夠進行排序。但是自定義的Person類並沒有給出排序的規則。即普通的自定義類不具備排序的功能,所以要實現Comparable接口,強制讓元素具備比較性,複寫compareTo方法。

    如何保證元素唯一性
    參考的就是比較方法(比如compareTo)的返回值是否是0。是0,就是重複元素,不存。

    注意:在進行比較時,如果判斷元素不唯一,比如,同姓名同年齡,才視爲同一個人。
    在判斷時,需要分主要條件和次要條件,當主要條件相同時,再判斷次要條件,按照次要條件排序。

    示例:往TreeSet集合存入上一節所描述的學生類對象。要求按照年齡進行排序

[java]  view plain  copy
  1. package ustc.lichunchun.domian;  
  2.   
  3. /* 
  4.  * 學生類本身繼承自Object類,具備一些方法。 
  5.  * 我們想要學生類具備比較的方法,就應該在學生類的基礎上進行功能的擴展。 
  6.  * 比較的功能已經在Comparable接口中定義下來了,學生類只需要實現Comparable接口即可。  
  7.  * 記住:需要對象具備比較性,只要讓對象實現comparable接口即可。 
  8.  */  
  9. public class Student implements Comparable{  
  10.     private String name;  
  11.     private int age;  
  12.     public Student() {  
  13.         super();  
  14.     }  
  15.     public Student(String name, int age) {  
  16.         super();  
  17.         this.name = name;  
  18.         this.age = age;  
  19.     }  
  20.     public String getName() {  
  21.         return name;  
  22.     }  
  23.     public void setName(String name) {  
  24.         this.name = name;  
  25.     }  
  26.     public int getAge() {  
  27.         return age;  
  28.     }  
  29.     public void setAge(int age) {  
  30.         this.age = age;  
  31.     }  
  32.     @Override  
  33.     public String toString() {  
  34.         return "Student [name=" + name + ", age=" + age + "]";  
  35.     }  
  36.     /* 
  37.     @Override 
  38.     public int hashCode() { 
  39.         final int prime = 31; 
  40.         int result = 1; 
  41.         result = prime * result + age; 
  42.         result = prime * result + ((name == null) ? 0 : name.hashCode()); 
  43.         return result; 
  44.     } 
  45.     @Override 
  46.     public boolean equals(Object obj) { 
  47.         if (this == obj) 
  48.             return true; 
  49.         if (obj == null) 
  50.             return false; 
  51.         if (getClass() != obj.getClass()) 
  52.             return false; 
  53.         Student other = (Student) obj; 
  54.         if (age != other.age) 
  55.             return false; 
  56.         if (name == null) { 
  57.             if (other.name != null) 
  58.                 return false; 
  59.         } else if (!name.equals(other.name)) 
  60.             return false; 
  61.         return true; 
  62.     } 
  63.     */  
  64.       
  65.      //覆蓋hashCode方法。根據對象自身的特點定義哈希值。  
  66.     public int hashCode(){  
  67.         final int NUMBER = 31;  
  68.         return name.hashCode()+ age*NUMBER;  
  69.     }  
  70.       
  71.      //需要定義對象自身判斷內容相同的依據。覆蓋equals方法。  
  72.     public boolean equals(Object obj){  
  73.         if (this == obj)  
  74.             return true;  
  75.         if(!(obj instanceof Student))  
  76.             throw new ClassCastException(obj.getClass().getName()+"類型錯誤");  
  77.         Student stu = (Student)obj;  
  78.         return this.name.equals(stu.name) && this.age == stu.age;  
  79.     }  
  80.       
  81.     //實現了comparable接口,學生就具備了比較功能。該功能是自然排序使用的方法。  
  82.     //自然排序就以年齡的升序排序爲主。  
  83.     //既然是同姓名同年齡是同一個人,視爲重複元素,要判斷的要素就有兩個。  
  84.     //既然是按照年齡進行排序。所以先判斷年齡,再判斷姓名。  
  85.     @Override  
  86.     public int compareTo(Object o) {  
  87.         Student stu = (Student)o;  
  88.         System.out.println(this.name+":"+this.age+"......"+stu.name+":"+stu.age);  
  89.         if(this.age > stu.age)  
  90.             return 1;  
  91.         if(this.age < stu.age)  
  92.             return -1;  
  93.         //return 0;//0表示重複元素,不存。  
  94.         return this.name.compareTo(stu.name);//進一步細化條件,只有姓名、年齡都一樣,纔是重複元素。  
  95.         /* 
  96.         主要條件: 
  97.         return this.age - stu.age; 
  98.         */  
  99.         /* 
  100.         主要條件+次要條件: 
  101.         int temp = this.age - stu.age; 
  102.         return temp==0?this.name.compareTo(stu.age):temp; 
  103.          */  
  104.     }  
  105. }  
[java]  view plain  copy
  1. package ustc.lichunchun.set.demo;  
  2.   
  3. import java.util.Iterator;  
  4. import java.util.Set;  
  5. import java.util.TreeSet;  
  6.   
  7. import ustc.lichunchun.domian.Student;  
  8.   
  9. public class TreeSetDemo {  
  10.   
  11.     public static void main(String[] args) {  
  12.         Set set = new TreeSet();  
  13.           
  14.         set.add(new Student("xiaoqiang",20));//java.lang.ClassCastException 類型轉換異常  
  15.                                             //問題:因爲學生要排序,就需要比較,而沒有定義比較方法,無法完成排序。  
  16.                                             //解決:add方法中實現比較功能,使用的是Comparable接口的比較方法。  
  17.                                             //comparable接口抽取並定義規則,強行對實現它的每個類的對象進行整體排序,實現我的類就得實現我的compareTo方法,否則不能創建對象。  
  18.         set.add(new Student("daniu",24));  
  19.         set.add(new Student("xiaoming",22));  
  20.         set.add(new Student("huanhuan",22));//根據複寫的compareTo方法,huanhuan和xiaoming兩個對象屬於重複元素(進一步細化條件之前,compareTo返回值爲0即視爲重複),又TreeSet容器不存重複元素,所以huanhuan沒有存進去。  
  21.         set.add(new Student("tudou",18));  
  22.         set.add(new Student("dahuang",19));  
  23.           
  24.         /*set.add(new Student("lisi02", 22));   
  25.         set.add(new Student("lisi007", 20));   
  26.         set.add(new Student("lisi09", 19));   
  27.         set.add(new Student("lisi08", 19));   
  28.         set.add(new Student("lisi11", 40));   
  29.         set.add(new Student("lisi16", 30));   
  30.         set.add(new Student("lisi12", 36));   
  31.         set.add(new Student("lisi10", 29));   
  32.         set.add(new Student("lisi22", 90));  
  33.         */  
  34.         for (Iterator it = set.iterator(); it.hasNext();) {  
  35.             Student stu = (Student)it.next();  
  36.             System.out.println(stu.getName()+":"+stu.getAge());  
  37.         }  
  38.     }  
  39. }  
     如何實現有序
    保證二叉樹只return一邊,比如:

[java]  view plain  copy
  1. public int compareTo(Object o){  
  2.     if (this.age == o.age)  
  3.         return 0;//保證TreeSet不存入自定義的重複元素。  
  4.     return 1;//保證添加的元素都存入二叉樹的右子樹。  
  5. }  

    TreeSet二叉樹建立過程
    TreeSet底層是二叉樹結構,二叉樹結構特點是可以排序。並且對二叉樹的建立過程內部優化,以減少比較次數。例子中將已排序的xiaoming:22作爲根節點,是基於折半的排序思想。xiaoqiang:20、xiaoming:22、daniu:24已經按照順序存好,爲了提高效率,在已排序的數組中去找一個新元素存放的位置,折半的方法最快。所以第四個進來的元素huanhuan:22會先和中間的xiaoming:22比較,然後確定往大的方向還是小的方向走。按照改進前的規則,huanhuan:22和xiaoming:22屬重複元素,不存。tudou:18進來,再和已排序的中間元素xiaoming:22比較。比xiaoming:22小,往小的方向走,接着和xiaoqiang:20比較,比它小,tudou:18放在xiaoqiang:20左子樹位置上。此時,已排序的依次爲:tudou:18、xiaoqiang:20、xiaoming:22、daniu:24。中間元素爲xiaoming:22。
dahuang:19先和xiaoming:22比,比它小;再和xiaoqiang:20比,比它小;接着和tudou:18比,比它大,放在tudou:18的右子樹上。
建樹完畢,TreeSet容器存入元素完畢。

    取出元素過程

    根節點的左子樹<右子樹,所以先遍歷左子樹,再根節點,最後右子樹即可。

    所以上述往TreeSet集合存入Student類元素的建樹、取元素過程的輸出結果爲:


    TreeSet第一種排序方式:需要元素具備比較功能。所以元素需要實現Comparable接口。覆蓋compareTo方法。

    需求中也有這樣一種情況,元素具備的比較功能不是所需要的,也就是說不想按照自然排序的方式,而是按照自定義的排序方式,對元素進行排序。而且,存儲到TreeSet中的元素萬一沒有比較功能,該如何排序呢?

    這時,就只能使用第二種排序方式--是讓集合具備比較功能,定義一個比較器。聯想到集合的構造函數,去查API。

    TreeSet第二種排序方式:需要集合具備比較功能,定義一個比較器。所以要實現java.util.Comparator<T>接口覆蓋compare方法。將Comparator接口的對象,作爲參數傳遞給TreeSet集合的構造函數。

    示例:自定義一個比較器,用來對學生對象按照姓名進行排序

    實現Comparator自定義比較器的代碼如下:

[java]  view plain  copy
  1. package ustc.lichunchun.comparator;  
  2.   
  3. import java.util.Comparator;  
  4.   
  5. import ustc.lichunchun.domian.Student;  
  6.   
  7. /** 
  8.  * 自定義一個比較器,用來對學生對象按照姓名進行排序。 
  9.  *  
  10.  * @author lichunchun 
  11.  */  
  12. public class ComparatorByName extends Object implements Comparator {  
  13.   
  14.     @Override  
  15.     public int compare(Object o1, Object o2) {  
  16.         Student s1 = (Student) o1;  
  17.         Student s2 = (Student) o2;  
  18.   
  19.         int temp = s1.getName().compareTo(s2.getName());  
  20.         return temp == 0 ? s1.getAge() - s2.getAge() : temp;  
  21.     }  
  22.       
  23.     //ComparatorByName類通過繼承Object類,已經複寫了Comparator接口的equals方法。  
  24.     //這裏的equals方法是用來判斷多個比較器是否相同。  
  25.     //如果程序中有多個比較器,這時實現Comparator的類就應該自己複寫equals方法,來判斷幾個比較器之間是否相同。  
  26. }  
      此時,再往TreeSet集合中存入學生類對象時,主要在TreeSet的構造函數中加入比較器參數,即可完成自定義排序。
[java]  view plain  copy
  1. package ustc.lichunchun.set.demo;  
  2.   
  3. import java.util.Iterator;  
  4. import java.util.Set;  
  5. import java.util.TreeSet;  
  6.   
  7. import ustc.lichunchun.comparator.ComparatorByName;  
  8. import ustc.lichunchun.domian.Student;  
  9.   
  10. public class TreeSetDemo2 {  
  11.   
  12.     public static void main(String[] args) {  
  13.   
  14.         //初始化TreeSet集合明確一個比較器。  
  15.         Set set = new TreeSet(new ComparatorByName());  
  16.           
  17.         set.add(new Student("xiaoqiang",20));  
  18.         set.add(new Student("daniu",24));  
  19.         set.add(new Student("xiaoming",22));  
  20.         set.add(new Student("tudou",18));  
  21.         set.add(new Student("daming",19));  
  22.         set.add(new Student("dahuang",19));  
  23.           
  24.         for (Iterator it = set.iterator(); it.hasNext();) {  
  25.             Student stu = (Student)it.next();  
  26.             System.out.println(stu.getName()+":"+stu.getAge());  
  27.         }  
  28.     }  
  29. }  
     TreeSet集合排序有兩種方式,Comparable和Comparator區別
    1.讓元素自身具備比較性,需要元素對象實現Comparable接口,覆蓋compareTo方法。
    2.讓集合自身具備比較性,需要定義一個實現了Comparator接口的比較器,並覆蓋compare方法,並將該類對象作爲實際參數傳遞給TreeSet集合的構造函數。
    3.容器使用Comparator比較器接口對元素進行排序,只要實現比較器對象就可以。
            -->降低了比較方式和集合之間的耦合性-->自定義比較器的方式更爲靈活。
       元素自身可以具備比較功能
            -->自然排序通常都作爲元素的默認排序。
    4.Comparable接口的compareTo方法,一個參數;Comparator接口的compare方法,兩個參數。

    List是數組或者鏈表結構,允許重複元素。
    HashSet是哈希表結構,查詢速度快。
    TreeSet是二叉樹數據結構。二叉樹結構可以實現排序,一堆數據只要存入二叉樹,自動完成排序。

    如果你堅持看完了本博文上面這部分內容,可以嘗試自己動手做下面這6個小練習:

[java]  view plain  copy
  1. package ustc.lichunchun.test;  
  2.   
  3. import java.util.Comparator;  
  4. import java.util.Set;  
  5. import java.util.TreeSet;  
  6.   
  7. import ustc.lichunchun.comparator.ComparatorByLength;  
  8.   
  9. /* 
  10.  * 練習1:將Person對象存儲到HashSet集合中,同姓名同年齡視爲同一個人,不存。(複寫Person類的hashCode、equals方法) 
  11.  *  
  12.  * 練習2:將Person對象存儲到TreeSet集合中,同姓名同年齡視爲同一個人,不存,姓名升序排序爲自然排序。(實現Comparable接口,複寫compareTo方法,姓名爲主要條件、年齡爲次要條件) 
  13.  *  
  14.  * 練習3:基於練習2,實現Person對象按照年齡升序排序。(實現Comparable接口,複寫compareTo方法,年齡爲主要條件) 
  15.  *  
  16.  * 練習4:對多個字符串(不重複)按照長度排序(由短到長)。(字符串中已複寫Comparable接口,但是是按照字典順序排序,無法使用。這裏應該實現Comparator比較器,複寫compare方法,創建對象實例傳參給TreeSet構造函數) 
  17.  *  
  18.  * 練習5:對多個字符串(重複),按照長度排序。(不可以使用Set。數組、List都可以解決這個問題) 
  19.  *  
  20.  * 練習6:通過LinkedList,定義一個堆棧數據結構。(利用addFirst、removeLast實現隊列,addFirst、removeFirst實現堆棧) 
  21.  */  
  22. public class Test1 {  
  23.   
  24.     public static void main(String[] args) {  
  25.         /* 
  26.         HashSet set = new HashSet(); 
  27.         TreeSet set = new TreeSet(); 
  28.         set.add(new Person("lisi",18)); 
  29.         set.add(new Person("wanger",18)); 
  30.         set.add(new Person("zengcen",10)); 
  31.         set.add(new Person("huanhuan",22)); 
  32.         set.add(new Person("wanger",18)); 
  33.         set.add(new Person("hehe",24)); 
  34.          
  35.         for (Iterator it = set.iterator(); it.hasNext();) { 
  36.             System.out.println(it.next()); 
  37.         } 
  38.         */  
  39.         sortStringByLength2();  
  40.     }  
  41.     /* 
  42.      * 練習4:對多個字符串(不重複)按照長度排序(由短到長)。 
  43.      * 思路: 
  44.      * 1.多個字符串,需要容器存儲。 
  45.      * 2.選擇哪個容器?字符串是對象,可以選擇集合,而且不重複,選擇set集合。 
  46.      * 3.還需要排序,可以選擇TreeSet集合。 
  47.      */  
  48.     public static void sortStringByLength(){  
  49.         //Set set = new TreeSet();//自然排序的方式。  
  50.         Set set = new TreeSet(new ComparatorByLength());//按照字符串長度排序。  
  51.         set.add("haha");  
  52.         set.add("abc");  
  53.         set.add("zz");  
  54.         set.add("nba");  
  55.         set.add("xixixi");  
  56.         for (Object obj : set) {  
  57.             System.out.println(obj);  
  58.         }  
  59.     }  
  60.       
  61.     /* 
  62.      * 練習5:對多個字符串(重複),按照長度排序。 
  63.      * 1.能使用TreeSet嗎?不能。 
  64.      * 2.可以存儲到數組、List。這裏先選擇數組。後面會講解List。 
  65.      */  
  66.     public static void sortStringByLength2(){  
  67.         String[] strs = {"nba","haha","abccc","zero","xixi","nba","abccc","cctv","zero"};  
  68.         //自然排序可以使用String類中的compareTo方法。  
  69.         //但是現在要的是長度排序,這就需要比較器。  
  70.         //定義一個按照長度排序的比較器對象。  
  71.         Comparator comp = new ComparatorByLength();  
  72.           
  73.         //排序就需要嵌套循環。位置置換。  
  74.         for(int x = 0; x < strs.length-1; x++){  
  75.             for(int y = x+1; y < strs.length; y++){  
  76.                 //if(strs[x].compareTo(strs[y] > 0)){//按照字典順序  
  77.                 if(comp.compare(strs[x], strs[y]) > 0)//按照長度順序  
  78.                     swap(strs,x,y);  
  79.             }  
  80.         }  
  81.         for(String s : strs){  
  82.             System.out.println(s);  
  83.         }  
  84.     }  
  85.     public static void swap(String[] strs, int x, int y){  
  86.         String temp = strs[x];  
  87.         strs[x] = strs[y];  
  88.         strs[y] = temp;  
  89.     }  
  90.       
  91. }  
       這其中的Person類,我已經幫你實現好了。微笑
[java]  view plain  copy
  1. package ustc.lichunchun.domian;  
  2.   
  3. public class Person implements Comparable{  
  4.     private String name;  
  5.     private int age;  
  6.     public Person() {  
  7.         super();  
  8.     }  
  9.     public Person(String name, int age) {  
  10.         super();  
  11.         this.name = name;  
  12.         this.age = age;  
  13.     }  
  14.     public String getName() {  
  15.         return name;  
  16.     }  
  17.     public void setName(String name) {  
  18.         this.name = name;  
  19.     }  
  20.     public int getAge() {  
  21.         return age;  
  22.     }  
  23.     public void setAge(int age) {  
  24.         this.age = age;  
  25.     }  
  26.     @Override  
  27.     public String toString() {  
  28.         return "Person [name=" + name + ", age=" + age + "]";  
  29.     }  
  30.     @Override  
  31.     public int hashCode() {  
  32.         final int prime = 31;  
  33.         int result = 1;  
  34.         result = prime * result + age;  
  35.         result = prime * result + ((name == null) ? 0 : name.hashCode());  
  36.         return result;  
  37.     }  
  38.     /* 
  39.      * 建立Person類自己的判斷對象是否相同的依據,必須要覆蓋Object類中的equals方法。 
  40.      */  
  41.     public boolean equals(Object obj) {  
  42.         //爲了提高效率,如果比較的對象是同一個,直接返回true即可。  
  43.         if(this == obj)  
  44.             return true;  
  45.           
  46.         if(!(obj instanceof Person))  
  47.                 throw new ClassCastException("類型錯誤");  
  48.         Person p = (Person)obj;  
  49.           
  50.         return this.name.equals(p.name) && this.age==p.age;  
  51.     }  
  52.     /*@Override 
  53.     public boolean equals(Object obj) { 
  54.         if (this == obj) 
  55.             return true; 
  56.         if (obj == null) 
  57.             return false; 
  58.         if (getClass() != obj.getClass()) 
  59.             return false; 
  60.         Person other = (Person) obj; 
  61.         if (age != other.age) 
  62.             return false; 
  63.         if (name == null) { 
  64.             if (other.name != null) 
  65.                 return false; 
  66.         } else if (!name.equals(other.name)) 
  67.             return false; 
  68.         return true; 
  69.     }*/  
  70.       
  71.     /*@Override//按照姓名升序爲自然排序 
  72.     public int compareTo(Object o) { 
  73.         Person p = (Person)o; 
  74.         int temp = this.getName().compareTo(p.getName()); 
  75.         return temp==0?this.getAge()-p.getAge():temp; 
  76.     }*/  
  77.     @Override//按照年齡升序爲自然排序  
  78.     public int compareTo(Object o) {  
  79.         Person p = (Person)o;  
  80.         int temp = this.getAge()-p.getAge();  
  81.         return temp==0?this.getName().compareTo(p.getName()):temp;  
  82.     }  
  83. }  
      按照字符串長度排序的自定義比較器,我也幫你實現好了哦。得意
[java]  view plain  copy
  1. package ustc.lichunchun.comparator;  
  2.   
  3. import java.util.Comparator;  
  4.   
  5. public class ComparatorByLength implements Comparator {  
  6.   
  7.     @Override  
  8.     public int compare(Object o1, Object o2) {  
  9.         // 對字符串按照長度比較。  
  10.   
  11.         // 向下轉型  
  12.         String s1 = (String) o1;  
  13.         String s2 = (String) o2;  
  14.         // 比較長度  
  15.         int temp = s1.length() - s2.length();  
  16.         // 長度相同,再按字典序比較  
  17.         return temp == 0 ? s1.compareTo(s2) : temp;  
  18.     }  
  19.   
  20. }  
  21. /* 
  22. 在二叉樹(TreeSet)結構中,該比較器的compare方法返回0,代表相同的重複元素,就不存了。 
  23. 在數組結構實現按長度排序中,該比較器的compare方法返回0,代表相同的重複元素,但只是不交換位置而已。 
  24. */  

使用Collection集合的技巧

    jdk1.2以後出現的集合框架中的常用子類對象,存在的規律。

    需要唯一嗎?
    需要:Set
        需要制定順序:
             需要:TreeSet
             不需要:HashSet
             但是想要一個和存儲一致的順序(有序):LinkedHashSet
    不需要:List
        需要頻繁增刪嗎?
             需要:LinkedList
             不需要:ArrayList

    如何記錄每一個容器的結構和所屬體系呢?看名字!
    List
        |--ArrayList
        |--LinkedList
    Set
        |--HashSet
        |--TreeSet

    前綴名是數據結構名,後綴名是所屬體系名。
    ArrayList:數組結構。看到數組,就知道查詢快,看到List,就知道可以重複。可以增刪改查。
    LinkedList:鏈表結構,增刪快。xxxFirst、xxxLast方法,xxx:add、get、remove
    HashSet:哈希表,查詢速度更快,就要想到唯一性、元素必須覆蓋hashCode、equals。不保證有序。看到Set,就知道不可以重複。
    LinkedHashSet:鏈表+哈希表。可以實現有序,因爲有鏈表。但保證元素唯一性。
    TreeSet:二叉樹,可以排序。就要想到兩種比較方式(兩個接口):一種是自然排序Comparable,一種是比較器Comparator。

    而且通常這些常用的集合容器都是不同步的。

Foreach語句

    JDK1.5特性:增強for循環。

    作用:用於遍歷Collection集合or數組。

    格式
    for(元素類型 變量:Collection容器or數組)
    {
    }

    傳統for循環和增強for循環有什麼區別呢
    增強for必須有被遍歷的目標。該目標只能是Collection、數組。不可以是Map。

    代碼示例:

[java]  view plain  copy
  1. package ustc.lichunchun.foreach;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Collection;  
  5. import java.util.Iterator;  
  6.   
  7. public class ForeachDemo {  
  8.   
  9.     public static void main(String[] args) {  
  10.           
  11.         Collection coll = new ArrayList();  
  12.         coll.add("abc1");  
  13.         coll.add("abc2");  
  14.         coll.add("abc3");  
  15.           
  16.         for (Object obj : coll) {  
  17.             System.out.println(obj);  
  18.         }  
  19.           
  20.         /*for (Iterator it = coll.iterator(); it.hasNext();) { 
  21.             Object obj = it.next(); 
  22.             System.out.println(obj);             
  23.         }*/  
  24.           
  25.         //對於數組的遍歷,如果不操作其角標,可以使用增強for循環;如果要操作角標,使用傳統的for。  
  26.         int[] arr = {23,15,32,78};  
  27.         for (int i : arr) {  
  28.             System.out.println("i = "+i);  
  29.         }  
  30.     }  
  31. }  
Enumeration<E>接口

     java.util.Enumeration:枚舉。具備枚舉取出方式的容器只有Vector。已被淘汰。舉例如下:

[java]  view plain  copy
  1. package ustc.lichunchun.enumeration;  
  2.   
  3. import java.util.Enumeration;  
  4. import java.util.Iterator;  
  5. import java.util.Vector;  
  6.   
  7. public class EnumerationDemo {  
  8.   
  9.     public static void main(String[] args) {  
  10.         /* 
  11.          * Enumeration:枚舉。 
  12.          * 具備枚舉取出方式的容器只有Vector。 
  13.          */  
  14.         Vector v = new Vector();  
  15.         v.add("abc1");  
  16.         v.add("abc2");  
  17.         v.add("abc3");  
  18.           
  19.         /*Enumeration en = v.elements(); 
  20.         while(en.hasMoreElements()){ 
  21.             System.out.println(en.nextElement()); 
  22.         }*/  
  23.           
  24.         //獲取枚舉。-->淘汰了  
  25.         for(Enumeration en = v.elements(); en.hasMoreElements();){  
  26.             System.out.println("enumeration: "+en.nextElement());  
  27.         }  
  28.           
  29.         //獲取迭代。-->好用。  
  30.         for (Iterator it = v.iterator(); it.hasNext();) {  
  31.             System.out.println("iterator: "+it.next());  
  32.         }  
  33.           
  34.         //獲取高級for。-->無角標,僅爲遍歷。  
  35.         for (Object obj : v) {  
  36.             System.out.println("foreach: "+obj);  
  37.         }  
  38.     }  
  39. }  

泛型

    接下來,我要介紹JDK1.5以後出現的新技術,集合框架中的重點--泛型

    在JDK1.4版本之前,容器什麼類型的對象都可以存儲。但是在取出時,需要用到對象的特有內容時,需要做向下轉型。但是對象的類型不一致,導致了向下轉型發生了ClassCastException異常。爲了避免這個問題,只能主觀上控制,往集合中存儲的對象類型保持一致。

    JDK1.5以後,解決了該問題。在定義集合時,就直接明確集合中存儲元素的具體類型。這樣,編譯器在編譯時,就可以對集合中存儲的對象類型進行檢查。一旦發現類型不匹配,就編譯失敗。這個技術就是泛型技術

[java]  view plain  copy
  1. package ustc.lichunchun.generic.demo;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Iterator;  
  5. import java.util.List;  
  6.   
  7. public class GenericDemo {  
  8.   
  9.     public static void main(String[] args) {  
  10.           
  11.         List list = new ArrayList();  
  12.           
  13.         list.add("abc");  
  14.         list.add(4);//list.add(Integer.valueOf(4));自動裝箱.  
  15.           
  16.         for (Iterator it = list.iterator(); it.hasNext();) {  
  17.               
  18.             System.out.println(it.next());  
  19.             //等價於:  
  20.             Object obj = it.next();  
  21.             System.out.println(obj.toString());  
  22.             //因爲String和Integer類都複寫了Object類的toString方法,所以可以這麼做。  
  23.               
  24.             String str = (String)it.next();  
  25.             System.out.println(str.length());  
  26.             //->java.lang.ClassCastException:java.lang.Integer cannot be cast to java.lang.String  
  27.         }  
  28.               
  29.         //爲了在運行時期不出現類型異常,可以在定義容器時,就明確容器中的元素的類型。-->泛型  
  30.           
  31.         List<String> list = new ArrayList<String>();  
  32.         list.add("abc");  
  33.         for (Iterator<String> it = list.iterator(); it.hasNext();) {  
  34.             String str = it.next();  
  35.             //class文件中怎麼保證it.next()返回的Object類型一定能夠變成String類型?  
  36.             //雖然class文件中,沒有泛型標識。但是在編譯時期就已經保證了元素類型的統一,一定都是某一類元素。  
  37.             //那麼在底層,就會有自動的相應類型轉換。這叫做泛型的補償。  
  38.             System.out.println(str.length());  
  39.         }  
  40.     }  
  41. }  
     泛型的擦除
    編譯器通過泛型對元素類型進行檢查,只要檢查通過,就會生成class文件,但在class文件中,就將泛型標識去掉了。
    泛型只在源代碼中體現。但是通過編譯後的程序,保證了容器中元素類型的一致。

    泛型的補償
    在運行時,通過獲取元素的類型進行轉換操作。不用使用者再強制轉換了。 

    泛型的好處
    1.將運行時期的問題,轉移到了編譯時期,可以更好的讓程序員發現問題並解決問題。
    2.避免了強制轉換、向下轉型的麻煩。

[java]  view plain  copy
  1. package ustc.lichunchun.generic.demo;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Iterator;  
  5. import java.util.List;  
  6.   
  7. public class GenericDemo2 {  
  8.   
  9.     public static void main(String[] args) {  
  10.           
  11.         //創建一個List集合,存儲整數。List ArraytList  
  12.         List<Integer> list = new ArrayList<Integer>();  
  13.           
  14.         list.add(5);//自動裝箱  
  15.         list.add(6);  
  16.           
  17.         for (Iterator<Integer> it = list.iterator(); it.hasNext();) {  
  18.             Integer integer = it.next();//使用了泛型後,it.next()返回的就是指定的元素類型。  
  19.             System.out.println(integer);  
  20.         }  
  21.     }  
  22. }  
      總結:泛型就是應用在編譯時期的一項安全機制。泛型技術是給編譯器使用的技術,用於編譯時期,確保了類型的安全。

    例子一枚

[java]  view plain  copy
  1. package ustc.lichunchun.domain;  
  2.   
  3. public class Person implements Comparable<Person> {  
  4.   
  5.     private String name;  
  6.     private int age;  
  7.   
  8.     public Person() {  
  9.         super();  
  10.     }  
  11.   
  12.     public Person(String name, int age) {  
  13.         super();  
  14.         this.name = name;  
  15.         this.age = age;  
  16.     }  
  17.   
  18.     public String getName() {  
  19.         return name;  
  20.     }  
  21.   
  22.     public void setName(String name) {  
  23.         this.name = name;  
  24.     }  
  25.   
  26.     public int getAge() {  
  27.         return age;  
  28.     }  
  29.   
  30.     public void setAge(int age) {  
  31.         this.age = age;  
  32.     }  
  33.   
  34.     @Override  
  35.     public String toString() {  
  36.         return "Person [name=" + name + ", age=" + age + "]";  
  37.     }  
  38.   
  39.     @Override  
  40.     public int compareTo(Person o) {  
  41.         int temp = this.getAge() - o.getAge();  
  42.         return temp == 0 ? this.getName().compareTo(o.getName()) : temp;  
  43.     }  
  44.   
  45.     @Override  
  46.     public int hashCode() {  
  47.         final int NUMBER = 31;  
  48.         return this.name.hashCode()+this.age*NUMBER;  
  49.     }  
  50.   
  51.     @Override  
  52.     public boolean equals(Object obj) {  
  53.         if (this == obj)  
  54.             return true;  
  55.         if(!(obj instanceof Person))  
  56.             throw new ClassCastException("類型不匹配");  
  57.         Person p = (Person)obj;  
  58.         return this.name.equals(p.name) && this.age == p.age;  
  59.     }  
  60.   
  61. }  

[java]  view plain  copy
  1. package ustc.lichunchun.generic.demo;  
  2.   
  3. import java.util.HashSet;  
  4. import java.util.Set;  
  5. import java.util.TreeSet;  
  6.   
  7. import ustc.lichunchun.comparator.ComparatorByName;  
  8. import ustc.lichunchun.domain.Person;  
  9.   
  10. public class GenericDemo3 {  
  11.   
  12.     public static void main(String[] args) {  
  13.           
  14.         Set<String> set = new TreeSet<String>();  
  15.           
  16.         set.add("abcd");  
  17.         set.add("aa");  
  18.         set.add("nba");  
  19.         set.add("cba");  
  20.           
  21.         for (String s : set) {  
  22.             System.out.println(s);  
  23.         }  
  24.           
  25.         //按照年齡排序  
  26.         Set<Person> set = new TreeSet<Person>();  
  27.         set.add(new Person("abcd",20));  
  28.         set.add(new Person("aa",26));  
  29.         set.add(new Person("nba",22));  
  30.         set.add(new Person("cba",24));  
  31.           
  32.         for(Person p: set){  
  33.             System.out.println(p);  
  34.         }  
  35.           
  36.         //按照姓名排序  
  37.         Set<Person> set = new TreeSet<Person>(new ComparatorByName());  
  38.         set.add(new Person("abcd",20));  
  39.         set.add(new Person("aa",26));  
  40.         set.add(new Person("nba",22));  
  41.         set.add(new Person("cba",24));  
  42.           
  43.         for(Person p: set){  
  44.             System.out.println(p);  
  45.         }  
  46.           
  47.         //HashSet不重複的實現  
  48.         Set<Person> set = new HashSet<Person>();  
  49.         set.add(new Person("aa",26));  
  50.         set.add(new Person("abcd",20));  
  51.         set.add(new Person("abcd",20));  
  52.         set.add(new Person("nba",22));  
  53.         set.add(new Person("nba",22));  
  54.         set.add(new Person("cba",24));  
  55.           
  56.         for(Person p: set){  
  57.             System.out.println(p);  
  58.         }  
  59.     }  
  60. }  

    泛型的表現
    泛型技術在集合框架中應用的範圍很大。

    什麼時候需要寫泛型呢

    當類中的操作的引用數據類型不確定的時候,以前用的Object來進行擴展的,現在可以用泛型來表示。這樣可以避免強轉的麻煩,而且將運行問題轉移到的編譯時期。

    只要看到類或者接口在描述時右邊定義<>,就需要泛型。其實是,容器在不明確操作元素的類型的情況下,對外提供了一個參數,用<>封裝。使用容器時,只要將具體的類型實參傳遞給該參數即可。說白了,泛型就是,傳遞類型參數。

    下面依次介紹泛型類、泛型方法、泛型接口。

    1. 泛型類 --> 泛型定義在類上

    首先,我們實現兩個繼承自Person類的子類,分別是Student類、Worker類,代碼如下:

[java]  view plain  copy
  1. package ustc.lichunchun.domain;  
  2.   
  3. public class Student extends Person {  
  4.   
  5.     public Student() {  
  6.         super();  
  7.     }  
  8.   
  9.     public Student(String name, int age) {  
  10.         super(name, age);  
  11.     }  
  12.   
  13.     @Override  
  14.     public String toString() {  
  15.         return "Student [name="+getName()+", age="+getAge()+"]";  
  16.     }  
  17. }  
[java]  view plain  copy
  1. package ustc.lichunchun.domain;  
  2.   
  3. public class Worker extends Person {  
  4.   
  5.     public Worker() {  
  6.         super();  
  7.     }  
  8.   
  9.     public Worker(String name, int age) {  
  10.         super(name, age);  
  11.     }  
  12.   
  13.     @Override  
  14.     public String toString() {  
  15.         return "Worker [name=" + getName() + ", age=" + getAge() + "]";  
  16.     }  
  17. }  
      需求:創建一個用於操作Student對象的工具類。對 對象進行設置和獲取。
[java]  view plain  copy
  1. class Tool1{  
  2.     private Student stu;  
  3.   
  4.     public Student getStu() {  
  5.         return stu;  
  6.     }  
  7.   
  8.     public void setStu(Student stu) {  
  9.         this.stu = stu;  
  10.     }  
  11. }  
       發現程序太有侷限性了,可不可以定義一個可以操作所有對象的工具呢?類型需要向上抽取。當要操作的對象類型不確定的時候,爲了擴展,可以使用Object類型來完成。
[java]  view plain  copy
  1. //JDk 1.4 類型向上抽取到Object-->向下轉型在運行時期報ClassCastException。  
  2. class Tool2{  
  3.     private Object obj;  
  4.   
  5.     public Object getObj() {  
  6.         return obj;  
  7.     }  
  8.   
  9.     public void setObj(Object obj) {  
  10.         this.obj = obj;  
  11.     }  
  12. }  
      但是這種方式有一些小弊端,會出現轉型,尤其是向下轉型容易在編譯時期看不見錯誤、運行時期發生ClassCastExccption。

    JDK1.5以後,新的解決方案:使用泛型。類型不確定時,可以對外提供參數。由使用者通過傳遞參數的形式完成類型的確定。

[java]  view plain  copy
  1. //JDK 1.5 在類定義時就明確參數。由使用該類的調用者,來傳遞具體的類型。  
  2. class Util<W>{//-->泛型類。  
  3.     private W obj;  
  4.   
  5.     public W getObj() {  
  6.         return obj;  
  7.     }  
  8.   
  9.     public void setObj(W obj) {  
  10.         this.obj = obj;  
  11.     }  
  12. }  

     利用泛型類,我們就可以直接在編譯時期及時發現程序錯誤,同時避免了向下轉型的麻煩。利用上述泛型類工具,示例代碼如下:

[java]  view plain  copy
  1. package ustc.lichunchun.generic.demo;  
  2.   
  3. import ustc.lichunchun.domain.Student;  
  4. import ustc.lichunchun.domain.Worker;  
  5.   
  6. public class GenericDemo4 {  
  7.   
  8.     public static void main(String[] args) {  
  9.         /* 
  10.          * 泛型1:泛型類-->泛型定義在類上。 
  11.          */  
  12.           
  13.         //JDK 1.4  
  14.         Tool2 tool = new Tool2();  
  15.         tool.setObj(new Worker());  
  16.         Student stu = (Student)tool.getObj();//異常-->java.lang.ClassCastException: Worker cannot be cast to Student  
  17.         System.out.println(stu);  
  18.           
  19.         //JDK 1.5  
  20.         Util<Student> util = new Util<Student>();  
  21.         //util.setObj(new Worker());//編譯報錯-->如果類型不匹配,直接編譯失敗。  
  22.         //Student stu = util.getObj();//避免了向下轉型。不用強制類型轉換。  
  23.         System.out.println(stu);  
  24.           
  25.         //總結:什麼時候定義泛型?  
  26.         //當類型不明確時,就應該使用泛型來表示,在類上定義參數,由調用者來傳遞實際類型參數。  
  27.     }  
  28. }  

    2. 泛型方法 --> 泛型定義在方法上。這裏只需要注意一點,如果靜態方法需要定義泛型,泛型只能定義在方法上。代碼示例如下:

[java]  view plain  copy
  1. package ustc.lichunchun.generic.demo;  
  2.   
  3. public class GenericDemo5 {  
  4.   
  5.     public static void main(String[] args) {  
  6.         /* 
  7.          * 泛型2:泛型方法-->泛型定義在方法上。 
  8.          */  
  9.         Demo1<String> d = new Demo1<String>();  
  10.         d.show("abc");  
  11.         //d.print(6);在類上明確類型後,錯誤參數類型在編譯時期就報錯。  
  12.         Demo1<Integer> d1 = new Demo1<Integer>();  
  13.         d1.print(6);  
  14.         //d1.show("abc");  
  15.         System.out.println("----------------");  
  16.           
  17.         Demo2<String> d2 = new Demo2<String>();  
  18.         d2.show("abc");  
  19.         d2.print("bcd");  
  20.         d2.print(6);  
  21.     }  
  22. }  
  23. class Demo1<W>{  
  24.     public void show(W w){  
  25.         System.out.println("show: "+w);  
  26.     }  
  27.     public void print(W w){  
  28.         System.out.println("print: "+w);  
  29.     }  
  30. }  
  31. class Demo2<W>{  
  32.     public void show(W w){  
  33.         System.out.println("show: "+w);  
  34.     }  
  35.     public <Q> void print(Q w){//-->泛型方法。某種意義上可以將Q理解爲Object。  
  36.         System.out.println("print: "+w);  
  37.     }  
  38.     /* 
  39.     public static void show(W w){//報錯-->靜態方法是無法訪問類上定義的泛型的。 
  40.                                 //因爲靜態方法優先於對象存在,而泛型的類型參數確定,需要對象明確。 
  41.         System.out.println("show: "+w); 
  42.     } 
  43.     */  
  44.     public static <A> void staticShow(A a){//如果靜態方法需要定義泛型,泛型只能定義在方法上。  
  45.         System.out.println("static show: "+a);  
  46.     }  
  47. }  

   3. 泛型接口--> 泛型定義在接口上。

[java]  view plain  copy
  1. package ustc.lichunchun.generic.demo;  
  2.   
  3. public class GenericDemo6 {  
  4.   
  5.     public static void main(String[] args) {  
  6.         /* 
  7.          * 泛型3:泛型接口-->泛型定義在接口上。 
  8.          */  
  9.         SubDemo d = new SubDemo();  
  10.         d.show("abc");  
  11.     }  
  12.       
  13. }  
  14. interface Inter<T>{//泛型接口。   
  15.     public void show(T t);  
  16. }  
  17. class InterImpl<W> implements Inter<W>{//依然不明確要操作什麼類型。  
  18.   
  19.     @Override  
  20.     public void show(W t) {  
  21.         System.out.println("show: "+t);  
  22.     }  
  23. }  
  24. class SubDemo extends InterImpl<String>{  
  25.       
  26. }  
  27. /* 
  28. interface Inter<T>{//泛型接口。  
  29.     public void show(T t); 
  30. } 
  31. class InterImpl implements Inter<String>{ 
  32.     @Override 
  33.     public void show(String t) { 
  34.     } 
  35. } 
  36. */  
       泛型通配符<?>

    可以解決當具體類型不確定的時候,這個通配符就是<?>

    當操作類型時,不需要使用類型的具體功能時,只使用Object類中的功能。那麼可以用 ? 通配符來表未知類型。

[java]  view plain  copy
  1. package ustc.lichunchun.generic.demo;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Collection;  
  5. import java.util.HashSet;  
  6. import java.util.Iterator;  
  7. import java.util.List;  
  8. import java.util.Set;  
  9.   
  10. import ustc.lichunchun.domain.Student;  
  11.   
  12. public class GenericDemo7 {  
  13.   
  14.     public static void main(String[] args) {  
  15.         /* 
  16.          * 通配符<?> --> 相當於<? extends Object> 
  17.          */  
  18.         List<String> list = new ArrayList<String>();  
  19.         list.add("abc1");  
  20.         list.add("abc2");  
  21.         list.add("abc3");  
  22.         printCollection(list);  
  23.           
  24.         Set<String> set = new HashSet<String>();  
  25.         set.add("haha");  
  26.         set.add("xixi");  
  27.         set.add("hoho");  
  28.         printCollection(set);  
  29.           
  30.         List<Student> list2 = new ArrayList<Student>();  
  31.         list2.add(new Student("abc1",21));  
  32.         list2.add(new Student("abc2",22));  
  33.         list2.add(new Student("abc3",23));  
  34.         list2.add(new Student("abc4",24));  
  35.         //Collection<Object> coll = new ArrayList<Student>();-->wrong-->左右不一樣,可能會出現類型不匹配  
  36.         //Collection<Student> coll = new ArrayList<Object>();-->wrong-->左右不一樣,可能會出現類型不匹配  
  37.         //Collection<?> coll = new ArrayList<Student>();-->right-->通配符  
  38.         printCollection(list2);  
  39.     }  
  40.   
  41.     /*private static void printCollection(List<String> list) { 
  42.         for (Iterator<String> it = list.iterator(); it.hasNext();) { 
  43.             String str = it.next(); 
  44.             System.out.println(str); 
  45.         } 
  46.     }*/  
  47.     /*private static void printCollection(Collection<String> coll) { 
  48.         for (Iterator<String> it = coll.iterator(); it.hasNext();) { 
  49.             String str = it.next(); 
  50.             System.out.println(str); 
  51.         } 
  52.     }*/  
  53.     private static void printCollection(Collection<?> coll) {//在不明確具體類型的情況下,可以使用通配符來表示。  
  54.         for (Iterator<?> it = coll.iterator(); it.hasNext();) {//技巧:迭代器泛型始終保持和具體集合對象一致的泛型。  
  55.             Object obj = it.next();  
  56.             System.out.println(obj);  
  57.         }  
  58.     }  
  59. }  
泛型的限定

    <? extends E>:接收E類型或者E的子類型對象。上限。一般存儲對象的時候用。比如添加元素 addAll。
    <? super E>:接收E類型或者E的父類型對象。下限。一般取出對象的時候用。比如比較器。

    示例代碼:

[java]  view plain  copy
  1. package ustc.lichunchun.generic.demo;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Collection;  
  5. import java.util.HashSet;  
  6. import java.util.Iterator;  
  7. import java.util.List;  
  8. import java.util.Set;  
  9.   
  10. import ustc.lichunchun.domain.Person;  
  11. import ustc.lichunchun.domain.Student;  
  12. import ustc.lichunchun.domain.Worker;  
  13.   
  14. public class GenericDemo8 {  
  15.   
  16.     public static void main(String[] args) {  
  17.         /* 
  18.          * 泛型的限定 
  19.          */  
  20.         List<Student> list = new ArrayList<Student>();  
  21.         list.add(new Student("abc1",21));  
  22.         list.add(new Student("abc2",22));  
  23.         list.add(new Student("abc3",23));  
  24.         list.add(new Student("abc4",24));  
  25.         printCollection(list);  
  26.           
  27.         Set<Worker> set = new HashSet<Worker>();  
  28.         set.add(new Worker("haha",23));  
  29.         set.add(new Worker("xixi",24));  
  30.         set.add(new Worker("hoho",21));  
  31.         set.add(new Worker("haha",29));  
  32.         printCollection(set);  
  33.     }  
  34.   
  35.     /* 
  36.      * 泛型的限定: 
  37.      * ? extends E :接收E類型或者E的子類型。-->泛型上限。 
  38.      * ? super E :接收E類型或者E的父類型。-->泛型下限。 
  39.      */  
  40.     private static void printCollection(Collection<? extends Person> coll) {//泛型的限定,支持一部分類型。  
  41.         for (Iterator<? extends Person> it = coll.iterator(); it.hasNext();) {  
  42.             Person obj = it.next();//就可以使用Person的特有方法了。  
  43.             System.out.println(obj.getName()+":"+obj.getAge());  
  44.         }  
  45.     }  
  46. }  
     程序結果:


    練習1:演示泛型上限在API中的體現。我們這裏使用的是TreeSet的構造函數:TreeSet<E>(Collection<? extends E> coll)

[java]  view plain  copy
  1. package ustc.lichunchun.generic.demo;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Collection;  
  5. import java.util.Iterator;  
  6. import java.util.TreeSet;  
  7.   
  8. import ustc.lichunchun.domain.Person;  
  9. import ustc.lichunchun.domain.Student;  
  10. import ustc.lichunchun.domain.Worker;  
  11.   
  12. public class GenericDemo9 {  
  13.   
  14.     public static void main(String[] args) {  
  15.         /* 
  16.          *  演示泛型限定在API中的體現。 
  17.          *  TreeSet的構造函數。 
  18.          *  TreeSet<E>(Collection<? extends E> coll); 
  19.          *   
  20.          *  什麼時候會用到上限呢? 
  21.          *  一般往集合存儲元素時。如果集合定義了E類型,通常情況下應該存儲E類型的對象。 
  22.          *  對於E的子類型的對象,E類型也可以接受(多態)。所以這時可以將泛型從E改爲 ? extends E. 
  23.          */  
  24.         Collection<Student> coll = new ArrayList<Student>();  
  25.         coll.add(new Student("abc1",21));  
  26.         coll.add(new Student("abc2",22));  
  27.         coll.add(new Student("abc3",23));  
  28.         coll.add(new Student("abc4",24));  
  29.           
  30.         Collection<Worker> coll2 = new ArrayList<Worker>();  
  31.         coll2.add(new Worker("abc11",21));  
  32.         coll2.add(new Worker("abc22",27));  
  33.         coll2.add(new Worker("abc33",35));  
  34.         coll2.add(new Worker("abc44",29));  
  35.           
  36.         TreeSet<Person> ts = new TreeSet<Person>(coll);//coll2 也可以傳進來。  
  37.         ts.add(new Person("abc8",21));  
  38.         ts.addAll(coll2);//addAll(Collection<? extends E> c);  
  39.         for (Iterator<Person> it = ts.iterator(); it.hasNext();) {  
  40.             Person person = it.next();  
  41.             System.out.println(person.getName());  
  42.         }  
  43.     }  
  44. }  
  45. //原理  
  46. class MyTreeSet<E>{  
  47.     MyTreeSet(){  
  48.           
  49.     }  
  50.     MyTreeSet(Collection<? extends E> c){  
  51.           
  52.     }  
  53. }  
     練習2:演示泛型下限在API中的體現。同樣,我們這裏使用的是另一個TreeSet的構造函數:TreeSet<E>(Comparator<? super E> comparator)
[java]  view plain  copy
  1. package ustc.lichunchun.generic.demo;  
  2.   
  3. import java.util.Comparator;  
  4. import java.util.Iterator;  
  5. import java.util.TreeSet;  
  6.   
  7. import ustc.lichunchun.domain.Person;  
  8. import ustc.lichunchun.domain.Student;  
  9. import ustc.lichunchun.domain.Worker;  
  10.   
  11. public class GenericDemo10 {  
  12.   
  13.     public static void main(String[] args) {  
  14.         /* 
  15.          * 演示泛型限定在API中的體現。 
  16.          * TreeSet的構造函數。 
  17.          * TreeSet<E>(Comparator<? super E> comparator) 
  18.          *  
  19.          * 什麼時候用到下限呢? 
  20.          * 當從容器中取出元素操作時,可以用E類型接收,也可以用E的父類型接收。 
  21.          *  
  22.          */  
  23.         //創建一個Student、Worker都能接收的比較器。  
  24.         Comparator<Person> comp = new Comparator<Person>() {//匿名內部類  
  25.             @Override  
  26.             public int compare(Person o1, Person o2) {//每次都是容器中的兩個元素過來進行比較。  
  27.                 int temp = o1.getAge()-o2.getAge();  
  28.                 return temp==0?o1.getName().compareTo(o2.getName()):temp;  
  29.             }  
  30.         };  
  31.           
  32.         TreeSet<Student> ts = new TreeSet<Student>(comp);  
  33.         ts.add(new Student("abc1",21));  
  34.         ts.add(new Student("abc2",28));  
  35.         ts.add(new Student("abc3",23));  
  36.         ts.add(new Student("abc4",25));  
  37.           
  38.         TreeSet<Worker> ts1 = new TreeSet<Worker>(comp);  
  39.         ts1.add(new Worker("abc11",21));  
  40.         ts1.add(new Worker("abc22",27));  
  41.         ts1.add(new Worker("abc33",22));  
  42.         ts1.add(new Worker("abc44",29));  
  43.           
  44.         for (Iterator<? extends Person> it = ts1.iterator(); it.hasNext();) {  
  45.             Person p = it.next();//多態  
  46.             System.out.println(p);  
相關文章
相關標籤/搜索