JavaSE實戰——API(中) 集合框架

    轉載請聲明出處: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中的基本功能。

package ustc.lichunchun.collection.demo;

import java.util.ArrayList;
import java.util.Collection;

public class CollectionDemo {
	public static void main(String[] args) {
		Collection coll = new ArrayList();
		methodDemo(coll);
		System.out.println("------------------");
		methodAllDemo();
	}
	/*
	 * 演示Collection中的基本功能。 
	 */
	public static void methodDemo(Collection coll){
		
		//1.添加元素。
		coll.add("abc1");
		coll.add("abc2");
		coll.add("abc3");
		
		//2.刪除
		coll.remove("abc2");//移除和添加元素 --> 會改變集合的長度 --> 集合裏面實際上存的是對象們的引用
		
		//3.清除。
		coll.clear();
		
		//4.判斷包含。
		System.out.println("contains: "+coll.contains("abc1"));//底層實現判斷用的是equals()
		
		System.out.println(coll);
	}
	
	/*
	 * 演示帶All的方法。
	 */
	public static void methodAllDemo(){
		
		//1.創建兩個容器。
		Collection c1 = new ArrayList();
		Collection c2 = new ArrayList();
		
		//2.添加元素。
		c1.add("abc1");
		c1.add("abc2");
		c1.add("abc3");
		c1.add("abc4");
		
		c2.add("abc2");
		c2.add("abc3");
		c2.add("abc5");
		
		//往c1中添加c2。
		c1.addAll(c2);
		
		//判斷c1中是否包含c2中的所有元素。
		boolean b = c1.containsAll(c2);
		System.out.println("b = "+b);
		
		//從c1中刪除c2。將c1中和c2相同的元素從c1中刪除。
		c1.removeAll(c2);
		
		//將c1中和c2不同的元素從c1中刪除。保留c1中和c2相同的元素。
		c1.retainAll(c2);
		System.out.println(c1);
	}
}

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

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

 樓主懂得思考,先表揚一下。下面將引用一段接口的說明,你可以看看:
9.2 Interface Members
The members of an interface are:Those members declared in the interface. 
Those members inherited from direct superinterfaces. 
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.
  
大致意思如下:
9.2 接口方法
  一個接口中的方法有:
  1).直接聲明在接口中的成員方法;
  2).直接從父類接口中繼承而來的方法;
  3).如果一個接口沒有直接的父類接口(也就是其自身就是頂層接口),並且在其沒有顯示聲明相關方法時,那該接口則會根據Object中所有的public的實例方法進行一一映射(比如toString,Hashcode等)。當然如果此接口顯示去聲明一個與Object簽名相同並且帶有final修飾的方法時,則會有編譯期錯誤。
  
所以:由超類聲明,子類來new。調用的最終是子類中定義的方法,如果子類沒有,則調用子類的父類方法。這存在一種向上追溯的過程。說明是完全正確的。
  
完全贊同!
這裏做一點補充。
根據這一條說明,在List list=new ArrayList()之後,在list當中將可以調用object當中所有聲明public的方法,而調用的方法實體是來自ArrayList的。而之所以沒有list不可以調用,clone()與finalize()方法,只是因爲它們是protected的。
學習了。
不過很好奇,33樓的大哥,這條如此原版的聲明出自哪個參考書籍呢?學java就該看這種資料啊。

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

Iterator<E>接口

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

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

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

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

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


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

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

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

package ustc.lichunchun.collection.demo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class IteratorDemo {

	public static void main(String[] args) {
		
		//1.創建集合。
		Collection coll = new ArrayList();
		
		coll.add("abc1");
		coll.add("abc2");
		coll.add("abc3");
		
		//方式一:獲取該容器的迭代器。
		Iterator it = coll.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
		
		//方式二:直接for+alt+/,選擇第三個。
		for (Iterator it = coll.iterator(); it.hasNext();) {
			System.out.println(it.next());
		}
		
		System.out.println(it.next());//abc1
		System.out.println(it.next());//abc2
		System.out.println(it.next());//abc3
		System.out.println(it.next());//java.util.NoSuchElementException 
	}
}
     爲了降低容器的數據結構和取出容器元素的方法之間的耦合性,把訪問、取出容器元素的容器的內部類進行共性抽取,即各種容器的相應內部類都實現了Iterator接口,實現了hasNext()、next()、remove()方法。例如如下截取自ArrayList類的iterator()方法的底層實現代碼
public Iterator<E> iterator() {
        return new Itr();//取出ArrayList容器中元素的迭代器功能,返回的是一個Itr()迭代器對象,也就是實現Iterator接口的內部類對象。
    }

    /**
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {//-->ArrayList容器的內部類,實現了Iterator迭代接口(迭代器),裏面有hasNext()、next()、remove()方法。
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        public boolean hasNext() {
            return cursor != size;
        }
	
		@SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
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的特有方法:

package ustc.lichunchun.list.demo;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ListDemo {

	public static void main(String[] args) {
		List list = new ArrayList();
		methodDemo(list);
	}
	/*
	 * 演示List特有的方法。
	 */
	public static void methodDemo(List list){
		//1.常規添加元素。
		list.add("abc1");
		list.add("abc2");
		list.add("abc3");
		
		//2.插入元素。
		list.add(1,"hehe");
		
		//3.刪除。
		list.remove(1);
		list.remove(1);
		
		//4.獲取。
		System.out.println(list.get(3));// java.lang.IndexOutOfBoundsException
		System.out.println(list.get(1));
		System.out.println(list.indexOf("abc3"));
		
		//5.修改。
		list.set(1,"keke");
		
		System.out.println(list);
		
		//6.取出集合中所有的元素。
		for (Iterator it = list.iterator(); it.hasNext();) {
			System.out.println("iterator: "+it.next());
		}
		
		//7.List集合特有的取出方式。遍歷。
		for (int i = 0; i < list.size(); i++) {
			System.out.println("get: "+list.get(i));
		}
	}
}
    5.獲取所有元素:
    ListIterator listIterator():list集合特有的迭代器。

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

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

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

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

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


package ustc.lichunchun.list.demo;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class ListIteratorDemo {

	public static void main(String[] args) {
		
		List list = new ArrayList();
		
		list.add("abc1");
		list.add("abc2");
		list.add("abc3");
		list.add("abc4");
		
		//需求:在遍歷的過程中,如果遍歷到abc2,添加一個元素haha
		for (Iterator it = list.iterator(); it.hasNext();) {
			Object obj = it.next();//java.util.ConcurrentModificationException 
			if(obj.equals("abc2")){
				list.add("haha");
			}
		}
		//上述代碼出現的問題:
		//迭代器it在操作容器元素,迭代過程中使用了集合對象list同時對元素進行操作。
		//產生迭代結果的不確定性,引發了併發修改異常。
		//解決思想:在迭代過程中,想要執行一些操作,使用迭代器的方法就可以了。

		//使用List集合特有的迭代器:ListIterator,通過List集合的方法listIterator()獲取該列表迭代器對象。
		//ListIterator可以實現在迭代過程中的增刪改查,還可以逆向遍歷。(底層使用了List集合的角標)
		//總結:在迭代過程中想要對列表List元素進行操作的時候,就要使用列表迭代器ListIterator.
		
		for (ListIterator it = list.listIterator(); it.hasNext();) {
			Object obj = it.next();
			if(obj.equals("abc2")){
				it.add("haha");
			}
		}
		System.out.println(list);//[abc1, abc2, haha, abc3, abc4]
	}
}
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纔有意義。

package ustc.lichunchun.list.test;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import ustc.lichunchun.domian.Person;

public class ArrayListTest {

	public static void main(String[] args) {
		
		//1.創建ArrayList集合對象。
		List list = new ArrayList();
		
		//2.添加Person類型的對象。
		Person p1 = new Person("lisi1", 21);
		Person p2 = new Person("lisi2", 22);
		
		list.add(p1);//add(Object obj)
		list.add(p2);
		list.add(new Person("lisi3", 23));
		
		//3.取出元素。
		for (Iterator it = list.iterator(); it.hasNext();) {
			//it.next():取出的元素都是Object類型的。需要用到具體對象內容時,需要向下轉型。
			Person p = (Person)it.next();
			System.out.println(p.getName()+":"+p.getAge());//如果不向下轉型,Object類對象沒有getName、getAge方法。
		}
	}
}
    練習2:定義功能,去除ArrayList集合中的重複元素。

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

package ustc.lichunchun.list.test;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class ArrayListTest2 {

	public static void main(String[] args) {
		/*
		 * 練習2:定義功能,去除ArrayList集合中的重複元素。
		 */
		List list = new ArrayList();
		list.add("abc1");
		list.add("abc4");
		list.add("abc2");
		list.add("abc1");
		list.add("abc4");
		list.add("abc4");
		list.add("abc2");
		list.add("abc1");
		list.add("abc4");
		list.add("abc2");
		
		System.out.println(list);//[abc1, abc4, abc2, abc1, abc4, abc4, abc2, abc1, abc4, abc2]
		singleElement2(list);
		System.out.println(list);//[abc1, abc4, abc2]
	}
	
	/*
	 * 取出重複元素方式一。
	 * 定義功能,取出重複元素。因爲List帶有角標,比較容易進行for循環。
	 */
	public static void singleElement(List list){
		for (int x = 0; x < list.size()-1; x++){
			Object obj = list.get(x);
			for(int y = x+1; y < list.size(); y++){
				if (obj.equals(list.get(y))){
					list.remove(y--);//記住:remove、add等方法,會改變原數組長度!注意角標變化。
				}
			}
		}
	}
	
	/*
	 * 取出重複元素方式二。
	 * 思路:
	 * 1.最後唯一性的元素也很多,可以先定義一個容器用於存儲這些唯一性的元素。
	 * 2.對原有容器進行元素的獲取,併到臨時容器中去判斷是否存在。容器本身就有這功能,判斷元素是否存在。
	 * 3.存在就不存儲,不存在就存儲。
	 * 4.遍歷完原容器後,臨時容器中存儲的就是唯一性的元素。
	 */
	public static void singleElement2(List list){
		//1.定義一個臨時容器
		List temp = new ArrayList();
		
		//2.遍歷原容器
		for (Iterator it = list.iterator(); it.hasNext();) {
			Object obj = (Object) it.next();
			
			//3.在臨時容器中判斷遍歷到的元素是否存在
			if(!temp.contains(obj))//contains()底層原理就是使用的equals(),而且這裏用的是String類複寫的equals。
				//如果不存在,就存儲到臨時容器中
				temp.add(obj);
		}
		//將原容器清空
		list.clear();
		//將臨時容器中的元素都存儲到原容器中
		list.addAll(temp);
	}
}
    練習3:ArrayList取出重複的自定義元素。

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

package ustc.lichunchun.domian;

public class Person{
	private String name;
	private int age;
	public Person() {
		super();
	}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	/*
	 * 建立Person類自己的判斷對象是否相同的依據,必須要覆蓋Object類中的equals方法。
	 */
	public boolean equals(Object obj) {
		//爲了提高效率,如果比較的對象是同一個,直接返回true即可。
		if(this == obj)
			return true;
		
		if(!(obj instanceof Person))
				throw new ClassCastException("類型錯誤");
		Person p = (Person)obj;
		
		return this.name.equals(p.name) && this.age==p.age;
	}
	/*@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}*/
}

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

package ustc.lichunchun.list.test;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import ustc.lichunchun.domian.Person;

public class ArrayListTest3 {

	public static void main(String[] args) {
		/*
		 * 練習3:ArrayList取出重複的自定義元素。
		 * 
		 * 記住:往集合裏面存儲自定義元素,該元素所屬類一定要覆蓋equals、toString方法!
		 */
		List list = new ArrayList();
		Person p = new Person("li",19);
		list.add(p);
		list.add(p);//存儲了一個地址相同的對象。在equals方法中直接先this==obj即可。
		list.add(new Person("li",20));
		list.add(new Person("li",23));
		list.add(new Person("li",26));
		list.add(new Person("li",23));
		list.add(new Person("li",26));
		list.add(new Person("li",20));
		
		System.out.println(list);
		singleElement(list);
		System.out.println(list);
	}
	public static void singleElement(List list){
		List temp = new ArrayList();
		for (Iterator it = list.iterator(); it.hasNext();) {
			Object obj = (Object) it.next();
			if(!temp.contains(obj))// --> contains()方法底層調用的是容器中元素對象的equals()方法!
				//這裏如果Person類自身不定義equals方法,就使用Object的equals方法,比較的就僅僅是地址了。
				temp.add(obj);
		}
		list.clear();
		list.addAll(temp);
	}
}
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();

package ustc.lichunchun.list.linkedlist;

import java.util.LinkedList;

public class LinkedListDemo {

	public static void main(String[] args) {
		//1.創建一個鏈表對象。
		LinkedList link = new LinkedList();
		
		//演示xxxFirst()、xxxLast()方法。
		
		//2.添加方法。
		link.addFirst("abc1");
		link.addFirst("abc2");
		link.addFirst("abc3");
		
		//3.獲取元素。
		System.out.println(link.getFirst());//abc3
		System.out.println(link.getFirst());//abc3
		
		//4.刪除元素。
		System.out.println(link.removeFirst());//abc3
		System.out.println(link.removeFirst());//abc2
		
		//5.取出link中所有元素。
		while(!link.isEmpty())
			System.out.println(link.removeLast());//removeFirst()
		link.contains("abc3");//false
	}
}
    練習:請通過LInkedList實現一個堆棧,或者隊列數據結構。

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

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

    隊列結構代碼如下:

package ustc.lichunchun.list.linkedlist;

import java.util.LinkedList;

/*
 * 描述一個隊列數據結構。內部使用的是LinkedList。
 */
public class MyQueue {
	private LinkedList link;

	MyQueue() {
		link = new LinkedList();
	}

	/**
	 * 添加元素的方法。
	 */
	public void myAdd(Object obj) {
		// 內部使用的是LinkedList的方法。
		link.addFirst(obj);
	}

	/**
	 * 獲取隊列元素的方法。
	 */
	public Object myGet() {
		return link.removeLast();
	}

	/**
	 * 集合中是否有元素的方法。
	 */
	public boolean isNull() {
		return link.isEmpty();
	}
}

    堆棧結構代碼如下:

package ustc.lichunchun.list.linkedlist;

import java.util.LinkedList;

/*
 *  實現一個堆棧結構。內部使用的是LinkedList。
 */
public class MyStack {
	private LinkedList link;

	MyStack() {
		link = new LinkedList();
	}

	public void myAdd(Object obj) {
		link.addFirst(obj);
	}

	public Object myGet() {
		return link.removeFirst();
	}

	public boolean isNull() {
		return link.isEmpty();
	}
}

    測試:

package ustc.lichunchun.list.linkedlist;

import java.util.LinkedList;

public class LinkedListTest {

	public static void main(String[] args) {
		/*
		 * 練習:請通過LInkedList實現一個堆棧,或者隊列數據結構。
		 * 堆棧:先進後出。First In Last Out	  FILO.
		 * 隊列:先進先出。First In First Out  FIFO.
		 */
		
		//1.創建自定義的隊列對象。
		MyQueue queue = new MyQueue();
		
		//2.添加元素。
		queue.myAdd("abc1");
		queue.myAdd("abc2");
		queue.myAdd("abc3");
		queue.myAdd("abc4");
		
		//3.獲取所有元素。先進先出。
		while(!queue.isNull())
			System.out.println(queue.myGet());
		System.out.println("--------------------------");
		
		//1.創建自定義的堆棧對象。
		MyStack stack = new MyStack();
		
		//2.添加元素。
		stack.myAdd("def5");
		stack.myAdd("def6");
		stack.myAdd("def7");
		stack.myAdd("def8");
		
		//3.獲取所有元素。先進後出。
		while(!stack.isNull())
			System.out.println(stack.myGet());
	}
}

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

需求分析:

需求:用戶登錄註冊案例。

按照如下的操作,可以讓我們更符號面向對象思想
	A:有哪些類呢?
	B:每個類有哪些東西呢?
	C:類與類之間的關係是什麼呢?
	
分析:
	A:有哪些類呢?
		用戶類
		測試類
	B:每個類有哪些東西呢?
		用戶類:
			成員變量:用戶名,密碼
			構造方法:無參構造
			成員方法:getXxx()/setXxx()
				       登錄,註冊
				       
			假如用戶類的內容比較對,將來維護起來就比較麻煩,爲了更清晰的分類,我們就把用戶又劃分成了兩類
				用戶基本描述類
					成員變量:用戶名,密碼
					構造方法:無參構造
					成員方法:getXxx()/setXxx()
				用戶操作類
					登錄,註冊
		測試類:
			main方法。
	C:類與類之間的關係是什麼呢?
		在測試類中創建用戶操作類和用戶基本描述類的對象,並使用其功能。
		
分包:
	A:功能劃分
	B:模塊劃分
	C:先按模塊劃分,再按功能劃分
	
今天我們選擇按照功能劃分:
	用戶基本描述類包 ustc.lichunchun.pojo
	用戶操作接口 ustc.lichunchun.dao
	用戶操作類包 ustc.lichunchun.dao.impl
		本文中是集合實現,後續會有IO實現、GUI實現和數據庫實現。
	用戶測試類 ustc.lichunchun.test
代碼如下:

package ustc.lichunchun.pojo;
/**
 * 這是用戶基本描述類
 * 
 * @author 李春春
 * @version V1.0
 *
 */
public class User {
	// 用戶名
	private String username;
	// 密碼
	private String password;

	public User() {
		super();
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}
package ustc.lichunchun.dao;

import ustc.lichunchun.pojo.User;
/**
 * 這時針對用戶進行操作的接口
 * 
 * @author 李春春
 * @version V1.0
 *
 */
public interface UserDao {
	/**
	 * 這是用戶登錄功能
	 * 
	 * @param username
	 *            用戶名
	 * @param password
	 *            密碼
	 * @return 返回登陸是否成功
	 */
	public abstract boolean isLogin(String username, String password);

	/**
	 * 這是用戶註冊功能
	 * 
	 * @param user
	 *            要註冊的用戶信息
	 */
	public abstract void regist(User user);
}
package ustc.lichunchun.dao.impl;

import java.util.ArrayList;

import ustc.lichunchun.dao.UserDao;
import ustc.lichunchun.pojo.User;
/**
 * 這是用戶操作的具體實現類(集合版)
 * 
 * @author 李春春
 * @version V1.0
 *
 */
public class UserDaoImpl implements UserDao {
	//爲了讓多個方法能夠使用同一個集合,就把集合定義爲成員變量。
	//爲了不讓外人看到,用private
	//爲了讓多個對象共享同一個成員變量,用static
	private static ArrayList<User> array = new ArrayList<User>();
	@Override
	public boolean isLogin(String username, String password) {
		//遍歷集合,獲取每一個用戶,並判斷用戶的用戶名和密碼是否和傳遞過來的匹配
		boolean flag = false;
		
		for(User u : array){
			if(u.getUsername().equalsIgnoreCase(username) && u.getPassword().equalsIgnoreCase(password)){
				flag = true;
				break;
			}
		}
		
		return flag;
	}

	@Override
	public void regist(User user) {
		//把用戶信息存入集合
		array.add(user);
	}
}
package ustc.lichunchun.test;

import java.util.Scanner;

import ustc.lichunchun.dao.UserDao;
import ustc.lichunchun.dao.impl.UserDaoImpl;
import ustc.lichunchun.game.GuessNumber;
import ustc.lichunchun.pojo.User;

/**
 * 用戶測試類
 * 
 * @author 李春春
 * @version V1.0
 * 
 * 新增加了兩個小問題:
 * A.多個對象共享同一個成員變量,用靜態
 * B.循環裏面如果有switch,並且在switch裏面有break,那麼結束的不是循環,而是switch語句
 *
 */
public class UserTest {

	public static void main(String[] args) {
		//爲了能夠回來
		while (true) {
			// 歡迎界面,給出選擇項
			System.out.println("--------------歡迎光臨--------------");
			System.out.println("1 登陸");
			System.out.println("2 註冊");
			System.out.println("3 退出");
			System.out.println("請輸入你的選擇:");
			//鍵盤錄入選擇,根據選擇做不同的操作
			Scanner sc = new Scanner(System.in);
			//爲了後面的錄入信息的方便,所有的數據錄入全部用字符串接收
			String choiceString = sc.nextLine();
			//switch語句的多個地方要使用,我就定義到外面
			UserDao ud = new UserDaoImpl();//多態
			//經過簡單的思考,我選擇了switch
			switch (choiceString) {
			case "1":
				//登陸界面,請輸入用戶名和密碼
				System.out.println("--------------登錄界面--------------");
				System.out.println("請輸入用戶名:");
				String username = sc.nextLine();
				System.out.println("請輸入密碼:");
				String password = sc.nextLine();
				//調用登錄功能
				boolean flag = ud.isLogin(username, password);
				if (flag) {
					System.out.println("登陸成功,可以開始玩遊戲了");
					System.out.println("你玩麼?y/n");
					while(true){
						String resultString = sc.nextLine();
						if(resultString.equalsIgnoreCase("y")){
							GuessNumber.start();
							System.out.println("你還玩麼?y/n");
						}else{
							break;
						}
					}
					System.out.println("謝謝使用,歡迎下次再來");
					System.exit(0);
					//break;這裏寫break,結束的是switch
				} else {
					System.out.println("用戶名或者密碼有誤,登錄失敗");
				}
				break;

			case "2":
				//註冊界面,請輸入用戶名和密碼
				System.out.println("--------------註冊界面--------------");
				System.out.println("請輸入用戶名:");
				String newUserName = sc.nextLine();
				System.out.println("請輸入密碼:");
				String newPassword = sc.nextLine();
				//把用戶名和密碼封裝到一個對象中
				User user = new User();
				user.setUsername(newUserName);
				user.setPassword(newPassword);
				//調用註冊功能
				ud.regist(user);
				System.out.println("註冊成功");
				break;
			case "3":
			default:
				System.out.println("謝謝使用,歡迎下次再來");
				System.exit(0);
			}
		}
	}
}
其中的猜數字遊戲代碼爲:

package ustc.lichunchun.game;

import java.util.Scanner;

/**
 * 這是猜數字小遊戲
 * 
 * @author 李春春
 *
 */
public class GuessNumber {
	private GuessNumber() {
	}

	public static void start() {
		int num = (int) (Math.random() * 100) + 1;
		int count = 0;
		while (true) {
			System.out.println("請輸入數據(1-100):");
			Scanner sc = new Scanner(System.in);
			int guessNum = sc.nextInt();
			count++;
			if (guessNum > num) {
				System.out.println("你猜的數據" + guessNum + "大了");
			} else if (guessNum < num) {
				System.out.println("你猜的數據" + guessNum + "小了");
			} else {
				System.out.println("恭喜你," + count + "次就猜中了");
				break;
			}
		}
	}
}
程序運行結果如下:

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

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

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

程序運行截圖:

Set<E>接口

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

    代碼示例:

package ustc.lichunchun.set.demo;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class HashSetDemo {

	public static void main(String[] args) {
		
		//1.創建一個Set容器對象。
		Set set = new HashSet();
		
		//Set set = new LinkedHashSet();如果改成LinkedHashSet,可以實現有序。
		
		//2.添加元素。
		set.add("haha");
		set.add("nba");
		set.add("abc");
		set.add("nba");
		set.add("heihei");
		
		//3.只能用迭代器取出。
		for (Iterator it = set.iterator(); it.hasNext();) {
			System.out.println(it.next());
		}
	}
}
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.將學生對象存儲到容器中。

package ustc.lichunchun.domian;

public class Student {
	private String name;
	private int age;
	public Student() {
		super();
	}
	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}
	/*
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	*/
	
	 //覆蓋hashCode方法。根據對象自身的特點定義哈希值。
	public int hashCode(){
		final int NUMBER = 31;
		return name.hashCode()+ age*NUMBER;
	}
	
	 //需要定義對象自身判斷內容相同的依據。覆蓋equals方法。
	public boolean equals(Object obj){
		if (this == obj)
			return true;
		if(!(obj instanceof Student))
			throw new ClassCastException(obj.getClass().getName()+"類型錯誤");
		Student stu = (Student)obj;
		return this.name.equals(stu.name) && this.age == stu.age;
	}
}
package ustc.lichunchun.set.demo;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import ustc.lichunchun.domian.Student;

public class HashSetTest {

	public static void main(String[] args) {
		/*
		 * 練習:往HashSet中存儲學生對象(姓名,年齡)。同姓名、同年齡視爲同一個人,不存。
		 * 1.描述學生。
		 * 2.定義容器。
		 * 3.將學生對象存儲到容器中。
		 * 
		 * 發現存儲了同姓名、同年齡的學生是可以的。
		 * 原因是每一次存儲學生對象,都先調用hashCode()方法獲取哈希值。
		 * 但此時調用的是Object類中的hashCode。所以同姓名同年齡了,但因爲是不同的對象,哈希值也不同。
		 * 這就是同姓名同年齡存入的原因。
		 * 
		 * 解決:
		 * 需要根據學生對象自身的特點來定義哈希值。
		 * 所以就需要覆蓋hashCode方法。
		 * 
		 * 發現,當hashCode返回值相同時,會調用equals方法比較兩個對象是否相等。
		 * 還是會出現同姓名同年齡的對象,因爲子類沒有複寫equals方法,
		 * 直接用Object類的equals方法僅僅比較了兩個對象的地址值。
		 * 這就是同姓名同年齡還會存入的原因。
		 * 
		 * 解決:
		 * 需要定義對象自身判斷內容相同的依據。
		 * 所以就需要覆蓋equals方法。
		 * 
		 * 效率問題:
		 * 儘量減少哈希算法求得的哈希值的衝突。減少equals方法的調用。
		 */
		//1.創建容器對象。
		Set set = new HashSet();
		
		//2.存儲學生對象。
		set.add(new Student("xiaoqiang",20));
		set.add(new Student("wangcai",27));
		set.add(new Student("xiaoming",22));
		set.add(new Student("xiaoqiang",20));
		set.add(new Student("daniu",24));
		set.add(new Student("xiaoming",22));
		
		//3.獲取所有學生。
		for (Iterator it = set.iterator(); it.hasNext();) {
			Student stu = (Student) it.next();
			System.out.println(stu.getName()+":"+stu.getAge());
		}
	}
}
    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

    代碼示例:

package ustc.lichunchun.set.demo;

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

import ustc.lichunchun.domian.Student;

public class TreeSetDemo {

	public static void main(String[] args) {
		Set set = new TreeSet();
		
		set.add("abc");
		set.add("heihei");
		set.add("nba");
		set.add("haha");
		set.add("heihei");
		
		for (Iterator it = set.iterator(); it.hasNext();) {
			System.out.println(it.next());
		}
	}
}
    程序輸出:


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

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

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

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

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

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

package ustc.lichunchun.domian;

/*
 * 學生類本身繼承自Object類,具備一些方法。
 * 我們想要學生類具備比較的方法,就應該在學生類的基礎上進行功能的擴展。
 * 比較的功能已經在Comparable接口中定義下來了,學生類只需要實現Comparable接口即可。 
 * 記住:需要對象具備比較性,只要讓對象實現comparable接口即可。
 */
public class Student implements Comparable{
	private String name;
	private int age;
	public Student() {
		super();
	}
	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}
	/*
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	*/
	
	 //覆蓋hashCode方法。根據對象自身的特點定義哈希值。
	public int hashCode(){
		final int NUMBER = 31;
		return name.hashCode()+ age*NUMBER;
	}
	
	 //需要定義對象自身判斷內容相同的依據。覆蓋equals方法。
	public boolean equals(Object obj){
		if (this == obj)
			return true;
		if(!(obj instanceof Student))
			throw new ClassCastException(obj.getClass().getName()+"類型錯誤");
		Student stu = (Student)obj;
		return this.name.equals(stu.name) && this.age == stu.age;
	}
	
	//實現了comparable接口,學生就具備了比較功能。該功能是自然排序使用的方法。
	//自然排序就以年齡的升序排序爲主。
	//既然是同姓名同年齡是同一個人,視爲重複元素,要判斷的要素就有兩個。
	//既然是按照年齡進行排序。所以先判斷年齡,再判斷姓名。
	@Override
	public int compareTo(Object o) {
		Student stu = (Student)o;
		System.out.println(this.name+":"+this.age+"......"+stu.name+":"+stu.age);
		if(this.age > stu.age)
			return 1;
		if(this.age < stu.age)
			return -1;
		//return 0;//0表示重複元素,不存。
		return this.name.compareTo(stu.name);//進一步細化條件,只有姓名、年齡都一樣,纔是重複元素。
		/*
		主要條件:
		return this.age - stu.age;
		*/
		/*
		主要條件+次要條件:
		int temp = this.age - stu.age;
		return temp==0?this.name.compareTo(stu.age):temp;
		 */
	}
}
package ustc.lichunchun.set.demo;

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

import ustc.lichunchun.domian.Student;

public class TreeSetDemo {

	public static void main(String[] args) {
		Set set = new TreeSet();
		
		set.add(new Student("xiaoqiang",20));//java.lang.ClassCastException 類型轉換異常
											//問題:因爲學生要排序,就需要比較,而沒有定義比較方法,無法完成排序。
											//解決:add方法中實現比較功能,使用的是Comparable接口的比較方法。
											//comparable接口抽取並定義規則,強行對實現它的每個類的對象進行整體排序,實現我的類就得實現我的compareTo方法,否則不能創建對象。
		set.add(new Student("daniu",24));
		set.add(new Student("xiaoming",22));
		set.add(new Student("huanhuan",22));//根據複寫的compareTo方法,huanhuan和xiaoming兩個對象屬於重複元素(進一步細化條件之前,compareTo返回值爲0即視爲重複),又TreeSet容器不存重複元素,所以huanhuan沒有存進去。
		set.add(new Student("tudou",18));
		set.add(new Student("dahuang",19));
		
		/*set.add(new Student("lisi02", 22));  
		set.add(new Student("lisi007", 20));  
		set.add(new Student("lisi09", 19));  
		set.add(new Student("lisi08", 19));  
		set.add(new Student("lisi11", 40));  
		set.add(new Student("lisi16", 30));  
		set.add(new Student("lisi12", 36));  
		set.add(new Student("lisi10", 29));  
		set.add(new Student("lisi22", 90)); 
		*/
		for (Iterator it = set.iterator(); it.hasNext();) {
			Student stu = (Student)it.next();
			System.out.println(stu.getName()+":"+stu.getAge());
		}
	}
}
    如何實現有序
    保證二叉樹只return一邊,比如:

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

    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自定義比較器的代碼如下:

package ustc.lichunchun.comparator;

import java.util.Comparator;

import ustc.lichunchun.domian.Student;

/**
 * 自定義一個比較器,用來對學生對象按照姓名進行排序。
 * 
 * @author lichunchun
 */
public class ComparatorByName extends Object implements Comparator {

	@Override
	public int compare(Object o1, Object o2) {
		Student s1 = (Student) o1;
		Student s2 = (Student) o2;

		int temp = s1.getName().compareTo(s2.getName());
		return temp == 0 ? s1.getAge() - s2.getAge() : temp;
	}
	
	//ComparatorByName類通過繼承Object類,已經複寫了Comparator接口的equals方法。
	//這裏的equals方法是用來判斷多個比較器是否相同。
	//如果程序中有多個比較器,這時實現Comparator的類就應該自己複寫equals方法,來判斷幾個比較器之間是否相同。
}
     此時,再往TreeSet集合中存入學生類對象時,主要在TreeSet的構造函數中加入比較器參數,即可完成自定義排序。
package ustc.lichunchun.set.demo;

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

import ustc.lichunchun.comparator.ComparatorByName;
import ustc.lichunchun.domian.Student;

public class TreeSetDemo2 {

	public static void main(String[] args) {

		//初始化TreeSet集合明確一個比較器。
		Set set = new TreeSet(new ComparatorByName());
		
		set.add(new Student("xiaoqiang",20));
		set.add(new Student("daniu",24));
		set.add(new Student("xiaoming",22));
		set.add(new Student("tudou",18));
		set.add(new Student("daming",19));
		set.add(new Student("dahuang",19));
		
		for (Iterator it = set.iterator(); it.hasNext();) {
			Student stu = (Student)it.next();
			System.out.println(stu.getName()+":"+stu.getAge());
		}
	}
}
    TreeSet集合排序有兩種方式,Comparable和Comparator區別
    1.讓元素自身具備比較性,需要元素對象實現Comparable接口,覆蓋compareTo方法。
    2.讓集合自身具備比較性,需要定義一個實現了Comparator接口的比較器,並覆蓋compare方法,並將該類對象作爲實際參數傳遞給TreeSet集合的構造函數。
    3.容器使用Comparator比較器接口對元素進行排序,只要實現比較器對象就可以。
            -->降低了比較方式和集合之間的耦合性-->自定義比較器的方式更爲靈活。
       元素自身可以具備比較功能
            -->自然排序通常都作爲元素的默認排序。
    4.Comparable接口的compareTo方法,一個參數;Comparator接口的compare方法,兩個參數。

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

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

package ustc.lichunchun.test;

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

import ustc.lichunchun.comparator.ComparatorByLength;

/*
 * 練習1:將Person對象存儲到HashSet集合中,同姓名同年齡視爲同一個人,不存。(複寫Person類的hashCode、equals方法)
 * 
 * 練習2:將Person對象存儲到TreeSet集合中,同姓名同年齡視爲同一個人,不存,姓名升序排序爲自然排序。(實現Comparable接口,複寫compareTo方法,姓名爲主要條件、年齡爲次要條件)
 * 
 * 練習3:基於練習2,實現Person對象按照年齡升序排序。(實現Comparable接口,複寫compareTo方法,年齡爲主要條件)
 * 
 * 練習4:對多個字符串(不重複)按照長度排序(由短到長)。(字符串中已複寫Comparable接口,但是是按照字典順序排序,無法使用。這裏應該實現Comparator比較器,複寫compare方法,創建對象實例傳參給TreeSet構造函數)
 * 
 * 練習5:對多個字符串(重複),按照長度排序。(不可以使用Set。數組、List都可以解決這個問題)
 * 
 * 練習6:通過LinkedList,定義一個堆棧數據結構。(利用addFirst、removeLast實現隊列,addFirst、removeFirst實現堆棧)
 */
public class Test1 {

	public static void main(String[] args) {
		/*
		HashSet set = new HashSet();
		TreeSet set = new TreeSet();
		set.add(new Person("lisi",18));
		set.add(new Person("wanger",18));
		set.add(new Person("zengcen",10));
		set.add(new Person("huanhuan",22));
		set.add(new Person("wanger",18));
		set.add(new Person("hehe",24));
		
		for (Iterator it = set.iterator(); it.hasNext();) {
			System.out.println(it.next());
		}
		*/
		sortStringByLength2();
	}
	/*
	 * 練習4:對多個字符串(不重複)按照長度排序(由短到長)。
	 * 思路:
	 * 1.多個字符串,需要容器存儲。
	 * 2.選擇哪個容器?字符串是對象,可以選擇集合,而且不重複,選擇set集合。
	 * 3.還需要排序,可以選擇TreeSet集合。
	 */
	public static void sortStringByLength(){
		//Set set = new TreeSet();//自然排序的方式。
		Set set = new TreeSet(new ComparatorByLength());//按照字符串長度排序。
		set.add("haha");
		set.add("abc");
		set.add("zz");
		set.add("nba");
		set.add("xixixi");
		for (Object obj : set) {
			System.out.println(obj);
		}
	}
	
	/*
	 * 練習5:對多個字符串(重複),按照長度排序。
	 * 1.能使用TreeSet嗎?不能。
	 * 2.可以存儲到數組、List。這裏先選擇數組。後面會講解List。
	 */
	public static void sortStringByLength2(){
		String[] strs = {"nba","haha","abccc","zero","xixi","nba","abccc","cctv","zero"};
		//自然排序可以使用String類中的compareTo方法。
		//但是現在要的是長度排序,這就需要比較器。
		//定義一個按照長度排序的比較器對象。
		Comparator comp = new ComparatorByLength();
		
		//排序就需要嵌套循環。位置置換。
		for(int x = 0; x < strs.length-1; x++){
			for(int y = x+1; y < strs.length; y++){
				//if(strs[x].compareTo(strs[y] > 0)){//按照字典順序
				if(comp.compare(strs[x], strs[y]) > 0)//按照長度順序
					swap(strs,x,y);
			}
		}
		for(String s : strs){
			System.out.println(s);
		}
	}
	public static void swap(String[] strs, int x, int y){
		String temp = strs[x];
		strs[x] = strs[y];
		strs[y] = temp;
	}
	
}
     這其中的Person類,我已經幫你實現好了。微笑
package ustc.lichunchun.domian;

public class Person implements Comparable{
	private String name;
	private int age;
	public Person() {
		super();
	}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	/*
	 * 建立Person類自己的判斷對象是否相同的依據,必須要覆蓋Object類中的equals方法。
	 */
	public boolean equals(Object obj) {
		//爲了提高效率,如果比較的對象是同一個,直接返回true即可。
		if(this == obj)
			return true;
		
		if(!(obj instanceof Person))
				throw new ClassCastException("類型錯誤");
		Person p = (Person)obj;
		
		return this.name.equals(p.name) && this.age==p.age;
	}
	/*@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}*/
	
	/*@Override//按照姓名升序爲自然排序
	public int compareTo(Object o) {
		Person p = (Person)o;
		int temp = this.getName().compareTo(p.getName());
		return temp==0?this.getAge()-p.getAge():temp;
	}*/
	@Override//按照年齡升序爲自然排序
	public int compareTo(Object o) {
		Person p = (Person)o;
		int temp = this.getAge()-p.getAge();
		return temp==0?this.getName().compareTo(p.getName()):temp;
	}
}
     按照字符串長度排序的自定義比較器,我也幫你實現好了哦。得意
package ustc.lichunchun.comparator;

import java.util.Comparator;

public class ComparatorByLength implements Comparator {

	@Override
	public int compare(Object o1, Object o2) {
		// 對字符串按照長度比較。

		// 向下轉型
		String s1 = (String) o1;
		String s2 = (String) o2;
		// 比較長度
		int temp = s1.length() - s2.length();
		// 長度相同,再按字典序比較
		return temp == 0 ? s1.compareTo(s2) : temp;
	}

}
/*
在二叉樹(TreeSet)結構中,該比較器的compare方法返回0,代表相同的重複元素,就不存了。
在數組結構實現按長度排序中,該比較器的compare方法返回0,代表相同的重複元素,但只是不交換位置而已。
*/

使用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循環語句(for/in語句)

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

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

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

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

    代碼示例:

package ustc.lichunchun.foreach;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class ForeachDemo {

	public static void main(String[] args) {
		
		Collection coll = new ArrayList();
		coll.add("abc1");
		coll.add("abc2");
		coll.add("abc3");
		
		for (Object obj : coll) {
			System.out.println(obj);
		}
		
		/*for (Iterator it = coll.iterator(); it.hasNext();) {
			Object obj = it.next();
			System.out.println(obj);			
		}*/
		
		//對於數組的遍歷,如果不操作其角標,可以使用增強for循環;如果要操作角標,使用傳統的for。
		int[] arr = {23,15,32,78};
		for (int i : arr) {
			System.out.println("i = "+i);
		}
	}
}

引入原因:替換Iterator複雜寫法,本質就是Iterator

foreach語句主要應用:
1、遍歷數組
2、遍歷Collection 集合對象

forin輸出 結果內存分析,見下圖:


代碼示例:

package cn.itcast.jdk5;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import org.junit.Test;

/**
 * for in 語句
 */
public class ForInTest {
	@Test
	public void demo1() {
		List<String> list = new ArrayList<String>();

		list.add("abc");
		list.add("def");
		list.add("qwe");

		// JDK5之前 兩種遍歷方式 :通過下標遍歷、通過Iterator
		Iterator<String> iterator = list.iterator();
		while (iterator.hasNext()) {
			System.out.println(iterator.next());
		}
		System.out.println("--------------------------------");
		for (Iterator iterator2 = list.iterator(); iterator2.hasNext();) {
			String string = (String) iterator2.next();
			System.out.println(string);
		}
		System.out.println("--------------------------------");

		// for in 簡化 Iterator --- for in 就是 Iterator
		for (String s : list) { // String s 表示 list中每一個字符串
			System.out.println(s);
		}
	}
}
如果一個對象 使用forin語句中,該對象 必須滿足兩個條件:
1、類 必須實現 Iterable 接口
2、類 實現 iterater 方法
* 編寫Car 讓 Car對象用於forin 語句
代碼示例:
package cn.itcast.jdk5;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import org.junit.Test;

public class ForInTest {
	@Test
	public void demo3() {
		// Car 用於 forin語句的
		Car car = new Car();
		for (String name : car) {
			System.out.println(name);
		}

		// 原理
		Iterator<String> iterator = car.iterator();
		while (iterator.hasNext()) {
			System.out.println(iterator.next());
		}
	}
}

// Car對象 可以用於 for in 語句
class Car implements Iterable<String> {
	String[] names = { "保時捷", "寶馬", "奧迪", "桑塔納", "大衆" };

	@Override
	public Iterator<String> iterator() {
		// 自定義迭代器
		return new MyIterator();
	}

	class MyIterator implements Iterator<String> {

		int index = 0; // 當前遍歷數組下標

		@Override
		public boolean hasNext() {
			if (index >= names.length) { // 證明下標無法取得元素
				return false;
			}
			return true;
		}

		@Override
		public String next() {
			String name = names[index];
			index++;
			return name;
		}

		@Override
		public void remove() {
		}

	}
}
注:for/in 實現 就是 Iterator !!!!!!!!!

移除練習 

練習:”abc”,”bcd”,”asf”,”ceg”,”daf”,”dfs” 移除所有包含a 字符串

使用迭代器和for/in 進行list循環 ,刪除元素時 : java.util.ConcurrentModificationException
1、解決方案:使用Iterator自帶 remove方法
2、如果只刪除一個元素,可以forin語句刪除元素後,通過break跳出循環
3、使用解決這類異常線程安全集合 CopyOnWriteArrayList<E>

forin刪除併發異常分析,見下圖:


代碼示例:

package cn.itcast.jdk5;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import org.junit.Test;

/**
 * for in 語句
 * 
 * @author seawind
 * 
 */
public class ForInTest {
	@Test
	public void demo6() {
		// 使用線程安全集合對象 ,在forin循環中刪除
		List<String> list = new CopyOnWriteArrayList<String>();
		list.add("abc");
		list.add("asf");
		list.add("bcd");
		list.add("daf");
		list.add("ceg");
		list.add("dfs");

		// 移除所有包含a 元素
		for (String s : list) {
			if (s.contains("a")) {
				list.remove(s);
			}
		}

		System.out.println(list);

	}

	@Test
	public void demo5() {
		// 如果在 for循環中只刪除一個元素
		List<String> list = new ArrayList<String>();
		list.add("abc");
		list.add("asf");
		list.add("bcd");
		list.add("daf");
		list.add("ceg");
		list.add("dfs");

		// 刪除 daf
		for (String s : list) {
			if (s.equals("daf")) {
				list.remove(s);// 刪除 ceg
				break;
			}
		}

		System.out.println(list);
	}

	@Test
	public void demo4() {
		// List 移除練習
		// ”abc”,”bcd”,”asf”,”ceg”,”daf”,”dfs”
		List<String> list = new ArrayList<String>();
		list.add("abc");
		list.add("asf");
		list.add("bcd");
		list.add("daf");
		list.add("ceg");
		list.add("dfs");
		// 遍歷集合移除所有包含字母”a” 的字符串
		// 遍歷List 三種寫法: 通過下標、通過Iterator 、通過forin語句

		// 通過下標
		// for (int i = 0; i < list.size(); i++) {
		// String s = list.get(i);
		// if (s.contains("a")) {
		// // 需要將s 從 list中移除
		// list.remove(s);
		// // 防止 元素被跳過
		// i--;
		// }
		// }

		// 通過for in
		for (String s : list) {
			if (s.contains("a")) {
				// list.remove(s); // 產生併發異常
				// 第一種解決方案 --- 使用迭代器自身刪除
			}
		}

		// 使用Iterator進行 List遍歷
		Iterator<String> iterator = list.iterator();
		while (iterator.hasNext()) {
			String s = iterator.next();
			if (s.contains("a")) {
				iterator.remove();
			}
		}

		System.out.println(list);
	}
}

Enumeration<E>接口

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

package ustc.lichunchun.enumeration;

import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;

public class EnumerationDemo {

	public static void main(String[] args) {
		/*
		 * Enumeration:枚舉。
		 * 具備枚舉取出方式的容器只有Vector。
		 */
		Vector v = new Vector();
		v.add("abc1");
		v.add("abc2");
		v.add("abc3");
		
		/*Enumeration en = v.elements();
		while(en.hasMoreElements()){
			System.out.println(en.nextElement());
		}*/
		
		//獲取枚舉。-->淘汰了
		for(Enumeration en = v.elements(); en.hasMoreElements();){
			System.out.println("enumeration: "+en.nextElement());
		}
		
		//獲取迭代。-->好用。
		for (Iterator it = v.iterator(); it.hasNext();) {
			System.out.println("iterator: "+it.next());
		}
		
		//獲取高級for。-->無角標,僅爲遍歷。
		for (Object obj : v) {
			System.out.println("foreach: "+obj);
		}
	}
}

泛型

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

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

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

package ustc.lichunchun.generic.demo;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class GenericDemo {

	public static void main(String[] args) {
		
		List list = new ArrayList();
		
		list.add("abc");
		list.add(4);//list.add(Integer.valueOf(4));自動裝箱.
		
		for (Iterator it = list.iterator(); it.hasNext();) {
			
			System.out.println(it.next());
			//等價於:
			Object obj = it.next();
			System.out.println(obj.toString());
			//因爲String和Integer類都複寫了Object類的toString方法,所以可以這麼做。
			
			String str = (String)it.next();
			System.out.println(str.length());
			//->java.lang.ClassCastException:java.lang.Integer cannot be cast to java.lang.String
		}
			
		//爲了在運行時期不出現類型異常,可以在定義容器時,就明確容器中的元素的類型。-->泛型
		
		List<String> list = new ArrayList<String>();
		list.add("abc");
		for (Iterator<String> it = list.iterator(); it.hasNext();) {
			String str = it.next();
			//class文件中怎麼保證it.next()返回的Object類型一定能夠變成String類型?
			//雖然class文件中,沒有泛型標識。但是在編譯時期就已經保證了元素類型的統一,一定都是某一類元素。
			//那麼在底層,就會有自動的相應類型轉換。這叫做泛型的補償。
			System.out.println(str.length());
		}
	}
}
    泛型的擦除
    編譯器通過泛型對元素類型進行檢查,只要檢查通過,就會生成class文件,但在class文件中,就將泛型標識去掉了。
    泛型只在源代碼中體現。但是通過編譯後的程序,保證了容器中元素類型的一致。

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

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

package ustc.lichunchun.generic.demo;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class GenericDemo2 {

	public static void main(String[] args) {
		
		//創建一個List集合,存儲整數。List ArraytList
		List<Integer> list = new ArrayList<Integer>();
		
		list.add(5);//自動裝箱
		list.add(6);
		
		for (Iterator<Integer> it = list.iterator(); it.hasNext();) {
			Integer integer = it.next();//使用了泛型後,it.next()返回的就是指定的元素類型。
			System.out.println(integer);
		}
	}
}
    總結:泛型就是應用在編譯時期的一項安全機制。泛型技術是給編譯器使用的技術,用於編譯時期,確保了類型的安全。

    例子一枚

package ustc.lichunchun.domain;

public class Person implements Comparable<Person> {

	private String name;
	private int age;

	public Person() {
		super();
	}

	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}

	@Override
	public int compareTo(Person o) {
		int temp = this.getAge() - o.getAge();
		return temp == 0 ? this.getName().compareTo(o.getName()) : temp;
	}

	@Override
	public int hashCode() {
		final int NUMBER = 31;
		return this.name.hashCode()+this.age*NUMBER;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if(!(obj instanceof Person))
			throw new ClassCastException("類型不匹配");
		Person p = (Person)obj;
		return this.name.equals(p.name) && this.age == p.age;
	}

}

package ustc.lichunchun.generic.demo;

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

import ustc.lichunchun.comparator.ComparatorByName;
import ustc.lichunchun.domain.Person;

public class GenericDemo3 {

	public static void main(String[] args) {
		
		Set<String> set = new TreeSet<String>();
		
		set.add("abcd");
		set.add("aa");
		set.add("nba");
		set.add("cba");
		
		for (String s : set) {
			System.out.println(s);
		}
		
		//按照年齡排序
		Set<Person> set = new TreeSet<Person>();
		set.add(new Person("abcd",20));
		set.add(new Person("aa",26));
		set.add(new Person("nba",22));
		set.add(new Person("cba",24));
		
		for(Person p: set){
			System.out.println(p);
		}
		
		//按照姓名排序
		Set<Person> set = new TreeSet<Person>(new ComparatorByName());
		set.add(new Person("abcd",20));
		set.add(new Person("aa",26));
		set.add(new Person("nba",22));
		set.add(new Person("cba",24));
		
		for(Person p: set){
			System.out.println(p);
		}
		
		//HashSet不重複的實現
		Set<Person> set = new HashSet<Person>();
		set.add(new Person("aa",26));
		set.add(new Person("abcd",20));
		set.add(new Person("abcd",20));
		set.add(new Person("nba",22));
		set.add(new Person("nba",22));
		set.add(new Person("cba",24));
		
		for(Person p: set){
			System.out.println(p);
		}
	}
}

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

    什麼時候需要寫泛型呢

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

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

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

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

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

package ustc.lichunchun.domain;

public class Student extends Person {

	public Student() {
		super();
	}

	public Student(String name, int age) {
		super(name, age);
	}

	@Override
	public String toString() {
		return "Student [name="+getName()+", age="+getAge()+"]";
	}
}
package ustc.lichunchun.domain;

public class Worker extends Person {

	public Worker() {
		super();
	}

	public Worker(String name, int age) {
		super(name, age);
	}

	@Override
	public String toString() {
		return "Worker [name=" + getName() + ", age=" + getAge() + "]";
	}
}
     需求:創建一個用於操作Student對象的工具類。對 對象進行設置和獲取。
class Tool1{
	private Student stu;

	public Student getStu() {
		return stu;
	}

	public void setStu(Student stu) {
		this.stu = stu;
	}
}
      發現程序太有侷限性了,可不可以定義一個可以操作所有對象的工具呢?類型需要向上抽取。當要操作的對象類型不確定的時候,爲了擴展,可以使用Object類型來完成。
//JDk 1.4 類型向上抽取到Object-->向下轉型在運行時期報ClassCastException。
class Tool2{
	private Object obj;

	public Object getObj() {
		return obj;
	}

	public void setObj(Object obj) {
		this.obj = obj;
	}
}
     但是這種方式有一些小弊端,會出現轉型,尤其是向下轉型容易在編譯時期看不見錯誤、運行時期發生ClassCastExccption。

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

//JDK 1.5 在類定義時就明確參數。由使用該類的調用者,來傳遞具體的類型。
class Util<W>{//-->泛型類。
	private W obj;

	public W getObj() {
		return obj;
	}

	public void setObj(W obj) {
		this.obj = obj;
	}
}

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

package ustc.lichunchun.generic.demo;

import ustc.lichunchun.domain.Student;
import ustc.lichunchun.domain.Worker;

public class GenericDemo4 {

	public static void main(String[] args) {
		/*
		 * 泛型1:泛型類-->泛型定義在類上。
		 */
		
		//JDK 1.4
		Tool2 tool = new Tool2();
		tool.setObj(new Worker());
		Student stu = (Student)tool.getObj();//異常-->java.lang.ClassCastException: Worker cannot be cast to Student
		System.out.println(stu);
		
		//JDK 1.5
		Util<Student> util = new Util<Student>();
		//util.setObj(new Worker());//編譯報錯-->如果類型不匹配,直接編譯失敗。
		//Student stu = util.getObj();//避免了向下轉型。不用強制類型轉換。
		System.out.println(stu);
		
		//總結:什麼時候定義泛型?
		//當類型不明確時,就應該使用泛型來表示,在類上定義參數,由調用者來傳遞實際類型參數。
	}
}

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

package ustc.lichunchun.generic.demo;

public class GenericDemo5 {

	public static void main(String[] args) {
		/*
		 * 泛型2:泛型方法-->泛型定義在方法上。
		 */
		Demo1<String> d = new Demo1<String>();
		d.show("abc");
		//d.print(6);在類上明確類型後,錯誤參數類型在編譯時期就報錯。
		Demo1<Integer> d1 = new Demo1<Integer>();
		d1.print(6);
		//d1.show("abc");
		System.out.println("----------------");
		
		Demo2<String> d2 = new Demo2<String>();
		d2.show("abc");
		d2.print("bcd");
		d2.print(6);
	}
}
class Demo1<W>{
	public void show(W w){
		System.out.println("show: "+w);
	}
	public void print(W w){
		System.out.println("print: "+w);
	}
}
class Demo2<W>{
	public void show(W w){
		System.out.println("show: "+w);
	}
	public <Q> void print(Q w){//-->泛型方法。某種意義上可以將Q理解爲Object。
		System.out.println("print: "+w);
	}
	/*
	public static void show(W w){//報錯-->靜態方法是無法訪問類上定義的泛型的。
								//因爲靜態方法優先於對象存在,而泛型的類型參數確定,需要對象明確。
		System.out.println("show: "+w);
	}
	*/
	public static <A> void staticShow(A a){//如果靜態方法需要定義泛型,泛型只能定義在方法上。
		System.out.println("static show: "+a);
	}
}

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

package ustc.lichunchun.generic.demo;

public class GenericDemo6 {

	public static void main(String[] args) {
		/*
		 * 泛型3:泛型接口-->泛型定義在接口上。
		 */
		SubDemo d = new SubDemo();
		d.show("abc");
	}
	
}
interface Inter<T>{//泛型接口。 
	public void show(T t);
}
class InterImpl<W> implements Inter<W>{//依然不明確要操作什麼類型。

	@Override
	public void show(W t) {
		System.out.println("show: "+t);
	}
}
class SubDemo extends InterImpl<String>{
	
}
/*
interface Inter<T>{//泛型接口。 
	public void show(T t);
}
class InterImpl implements Inter<String>{
	@Override
	public void show(String t) {
	}
}
*/
      泛型通配符<?>

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

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

package ustc.lichunchun.generic.demo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import ustc.lichunchun.domain.Student;

public class GenericDemo7 {

	public static void main(String[] args) {
		/*
		 * 通配符<?> --> 相當於<? extends Object>
		 */
		List<String> list = new ArrayList<String>();
		list.add("abc1");
		list.add("abc2");
		list.add("abc3");
		printCollection(list);
		
		Set<String> set = new HashSet<String>();
		set.add("haha");
		set.add("xixi");
		set.add("hoho");
		printCollection(set);
		
		List<Student> list2 = new ArrayList<Student>();
		list2.add(new Student("abc1",21));
		list2.add(new Student("abc2",22));
		list2.add(new Student("abc3",23));
		list2.add(new Student("abc4",24));
		//Collection<Object> coll = new ArrayList<Student>();-->wrong-->左右不一樣,可能會出現類型不匹配
		//Collection<Student> coll = new ArrayList<Object>();-->wrong-->左右不一樣,可能會出現類型不匹配
		//Collection<?> coll = new ArrayList<Student>();-->right-->通配符
		printCollection(list2);
	}

	/*private static void printCollection(List<String> list) {
		for (Iterator<String> it = list.iterator(); it.hasNext();) {
			String str = it.next();
			System.out.println(str);
		}
	}*/
	/*private static void printCollection(Collection<String> coll) {
		for (Iterator<String> it = coll.iterator(); it.hasNext();) {
			String str = it.next();
			System.out.println(str);
		}
	}*/
	private static void printCollection(Collection<?> coll) {//在不明確具體類型的情況下,可以使用通配符來表示。
		for (Iterator<?> it = coll.iterator(); it.hasNext();) {//技巧:迭代器泛型始終保持和具體集合對象一致的泛型。
			Object obj = it.next();
			System.out.println(obj);
		}
	}
}
泛型的限定

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

    示例代碼:

package ustc.lichunchun.generic.demo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import ustc.lichunchun.domain.Person;
import ustc.lichunchun.domain.Student;
import ustc.lichunchun.domain.Worker;

public class GenericDemo8 {

	public static void main(String[] args) {
		/*
		 * 泛型的限定
		 */
		List<Student> list = new ArrayList<Student>();
		list.add(new Student("abc1",21));
		list.add(new Student("abc2",22));
		list.add(new Student("abc3",23));
		list.add(new Student("abc4",24));
		printCollection(list);
		
		Set<Worker> set = new HashSet<Worker>();
		set.add(new Worker("haha",23));
		set.add(new Worker("xixi",24));
		set.add(new Worker("hoho",21));
		set.add(new Worker("haha",29));
		printCollection(set);
	}

	/*
	 * 泛型的限定:
	 * ? extends E :接收E類型或者E的子類型。-->泛型上限。
	 * ? super E :接收E類型或者E的父類型。-->泛型下限。
	 */
	private static void printCollection(Collection<? extends Person> coll) {//泛型的限定,支持一部分類型。
		for (Iterator<? extends Person> it = coll.iterator(); it.hasNext();) {
			Person obj = it.next();//就可以使用Person的特有方法了。
			System.out.println(obj.getName()+":"+obj.getAge());
		}
	}
}
    程序結果:


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

package ustc.lichunchun.generic.demo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.TreeSet;

import ustc.lichunchun.domain.Person;
import ustc.lichunchun.domain.Student;
import ustc.lichunchun.domain.Worker;

public class GenericDemo9 {

	public static void main(String[] args) {
		/*
		 *  演示泛型限定在API中的體現。
		 *  TreeSet的構造函數。
		 *  TreeSet<E>(Collection<? extends E> coll);
		 *  
		 *  什麼時候會用到上限呢?
		 *  一般往集合存儲元素時。如果集合定義了E類型,通常情況下應該存儲E類型的對象。
		 *  對於E的子類型的對象,E類型也可以接受(多態)。所以這時可以將泛型從E改爲 ? extends E.
		 */
		Collection<Student> coll = new ArrayList<Student>();
		coll.add(new Student("abc1",21));
		coll.add(new Student("abc2",22));
		coll.add(new Student("abc3",23));
		coll.add(new Student("abc4",24));
		
		Collection<Worker> coll2 = new ArrayList<Worker>();
		coll2.add(new Worker("abc11",21));
		coll2.add(new Worker("abc22",27));
		coll2.add(new Worker("abc33",35));
		coll2.add(new Worker("abc44",29));
		
		TreeSet<Person> ts = new TreeSet<Person>(coll);//coll2 也可以傳進來。
		ts.add(new Person("abc8",21));
		ts.addAll(coll2);//addAll(Collection<? extends E> c);
		for (Iterator<Person> it = ts.iterator(); it.hasNext();) {
			Person person = it.next();
			System.out.println(person.getName());
		}
	}
}
//原理
class MyTreeSet<E>{
	MyTreeSet(){
		
	}
	MyTreeSet(Collection<? extends E> c){
		
	}
}
    練習2:演示泛型下限在API中的體現。同樣,我們這裏使用的是另一個TreeSet的構造函數:TreeSet<E>(Comparator<? super E> comparator)
package ustc.lichunchun.generic.demo;

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

import ustc.lichunchun.domain.Person;
import ustc.lichunchun.domain.Student;
import ustc.lichunchun.domain.Worker;

public class GenericDemo10 {

	public static void main(String[] args) {
		/*
		 * 演示泛型限定在API中的體現。
		 * TreeSet的構造函數。
		 * TreeSet<E>(Comparator<? super E> comparator)
		 * 
		 * 什麼時候用到下限呢?
		 * 當從容器中取出元素操作時,可以用E類型接收,也可以用E的父類型接收。
		 * 
		 */
		//創建一個Student、Worker都能接收的比較器。
		Comparator<Person> comp = new Comparator<Person>() {//匿名內部類
			@Override
			public int compare(Person o1, Person o2) {//每次都是容器中的兩個元素過來進行比較。
				int temp = o1.getAge()-o2.getAge();
				return temp==0?o1.getName().compareTo(o2.getName()):temp;
			}
		};
		
		TreeSet<Student> ts = new TreeSet<Student>(comp);
		ts.add(new Student("abc1",21));
		ts.add(new Student("abc2",28));
		ts.add(new Student("abc3",23));
		ts.add(new Student("abc4",25));
		
		TreeSet<Worker> ts1 = new TreeSet<Worker>(comp);
		ts1.add(new Worker("abc11",21));
		ts1.add(new Worker("abc22",27));
		ts1.add(new Worker("abc33",22));
		ts1.add(new Worker("abc44",29));
		
		for (Iterator<? extends Person> it = ts1.iterator(); it.hasNext();) {
			Person p = it.next();//多態
			System.out.println(p);
		}
	}

}
//原理
class YouTreeSet<E>{
	YouTreeSet(Comparator<? super E> comparator){
		
	}
}
    泛型的細節

    1.泛型到底代表什麼類型取決於調用者傳入的類型,如果沒傳,默認是Object類型;
    2.使用帶泛型的類創建對象時,等式兩邊指定的泛型必須一致;
       原因:編譯器檢查對象調用方法時只看變量,然而程序運行期間調用方法時就要考慮對象具體類型了;
    3.等式兩邊可以在任意一邊使用泛型,在另一邊不使用(考慮向後兼容);

ArrayList<String> al = new ArrayList<Object>();  //錯
//要保證左右兩邊的泛型具體類型一致就可以了,這樣不容易出錯。

ArrayList<? extends Object> al = new ArrayList<String>();
al.add("aa");  //錯
//因爲集合具體對象中既可存儲String,也可以存儲Object的其他子類,所以添加具體的類型對象不合適,類型檢查會出現安全問題。
// ?extends Object 代表Object的子類型不確定,怎麼能添加具體類型的對象呢?

public static void method(ArrayList<? extends Object> al) {
	al.add("abc");  //錯
	//只能對al集合中的元素調用Object類中的方法,具體子類型的方法都不能用,因爲子類型不確定。
}
Map<K, V>接口

    java.util.Map<K,V>接口,將鍵映射到值的對象。一個映射不能包含重複的鍵;每個鍵最多隻能映射到一個值。要保證鍵的唯一性-->Set。值可以重複-->Collection。

    Map:雙列集合,一次存一對,鍵值對。
        |--Hashtable:底層是哈希表數據結構,是線程同步的,不允許存儲null鍵,null值。
               |--Properties:用來存儲鍵值對型的配置文件的信息,可以和IO技術相結合。
        |--HashMap:底層是哈希表數據結構,是線程不同步的,允許存儲null鍵,null值。替代了Hashtable。
        |--TreeMap:底層是二叉樹結構,線程不同步的。可以對map集合中的鍵進行指定順序的排序。

    揭祕:HashSet、TreeSet的底層是用HashMap、TreeMap實現的,只操作鍵,就是Set集合。

    Map集合存儲和Collection有着很大不同
    Collection一次存一個元素;Map一次存一對元素。
    Collection是單列集合;Map是雙列集合。
    Map中的存儲的一對元素:一個是鍵,一個是值,鍵與值之間有對應(映射)關係。
    特點:要保證map集合中鍵的唯一性

    Map接口中的共性功能

    1.添加:
        v put(key, value):當存儲的鍵相同時,新的值會替換老的值,並將老值返回。如果鍵沒有重複,返回null。
        putAll(Map<k,v> map);

    2.刪除:
        void clear():清空
        v remove(key):刪除指定鍵- -> 會改變集合長度

    3.判斷:
        boolean containsKey(Object key):是否包含key
        boolean containsValue(Object value):是否包含value
        boolean isEmpty();

    4.取出:
        v get(key):通過指定鍵獲取對應的值。如果返回null,可以判斷該鍵不存在。
                             當然有特殊情況,就是在hashmap集合中,是可以存儲null鍵null值的。
        int size():返回長度。

    代碼示例:

package ustc.lichunchun.map;

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

public class MapDemo {

	public static void main(String[] args) {
		/*
		 * 需求:Map集合中存儲學號、姓名。
		 */
		Map<Integer, String> map = new HashMap<Integer, String>();
		methodDemo(map);
	}
	public static void methodDemo(Map<Integer, String> map){
		
		//1.存儲鍵值對。如果鍵相同,會出現值覆蓋。
		System.out.println(map.put(3, "xiaoqiang"));
		System.out.println(map.put(3, "erhu"));
		map.put(7, "wangcai");
		map.put(2, "daniu");
		
		//2.移除。-->會改變長度。
		//System.out.println(map.remove(7));
		
		//3.獲取。
		System.out.println(map.get(7));
		
		System.out.println(map);
	}
}
      5.想要獲取Map中的所有元素:
    原理:map中是沒有迭代器的,collection具備迭代器,只要將map集合轉成Set集合,可以使用迭代器了。之所以轉成set,是因爲map集合具備着鍵的唯一性,其實set集合就來自於map,set集合底層其實用的就是map的方法。

    把Map集合轉成Set的方法:

    方式1: Set keySet();

    可以將map集合中的鍵都取出存放到set集合中。對set集合進行迭代。迭代完成,再通過get方法對獲取到的鍵進行值的獲取。

Set keySet = map.keySet();
Iterator it = keySet.iterator();
while(it.hasNext()) {
  Object key = it.next();
  Object value = map.get(key);
  System.out.println(key+":"+value);
}

     方式2: Set entrySet(); 

     取的是鍵和值的映射關係。Map.Entry:其實就是一個Map接口中的內部接口。爲什麼要定義在map內部呢?entry是訪問鍵值關係的入口,是map的入口,訪問的是map中的鍵值對。

Set entrySet = map.entrySet();
Iterator it = entrySet.iterator();
while(it.hasNext()) {
  Map.Entry  me = (Map.Entry)it.next();
  System.out.println(me.getKey()+"::::"+me.getValue());
}
    示例代碼如下所示:
package ustc.lichunchun.map;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class MapDemo2 {

	public static void main(String[] args) {
		/*
		 * 取出Map中所有的元素。 map存儲姓名---歸屬地。
		 */
		Map<String, String> map = new HashMap<String, String>();
		
		map.put("xiaoqiang", "beijing");
		map.put("wangcai", "funiushan");
		map.put("daniu", "heifengzhai");
		map.put("erhu", "wohudong");
		map.put("zhizunbao", "funiushan");
		
		//System.out.println(map.get("wangcai"));
		
		/*
		//演示keySet(); 取出所有的鍵,並存儲到Set集合中。
		Set<String> keySet = map.keySet();
		
		//Map集合沒有迭代器。但是可以將Map集合轉成Set集合,在使用迭代器就ok了。
		for (Iterator<String> it = keySet.iterator(); it.hasNext();) {
			
			String key = it.next();
			String value = map.get(key);
			System.out.println(key+":"+value);
			
		}
		
		//演示entrySet(); Map.Entry:其實就是一個Map接口中的內部接口。
		Set<Map.Entry<String, String>> entrySet = map.entrySet();
		
		for (Iterator<Map.Entry<String, String>> it = entrySet.iterator(); it.hasNext();) {
			Map.Entry<String, String> me = it.next();
			String key = me.getKey();
			String value = me.getValue();
			System.out.println(key+"::"+value);
		}
		 */
		
		//演示values(); 獲取所有的值。
		Collection<String> values = map.values();
		
		for (Iterator<String> it = values.iterator(); it.hasNext();) {
			String value = it.next();
			System.out.println(value);		
		}
	}
}
//原理
interface MyMap{//-->鍵值對
	
	//entry就是map接口中的內部接口。
	public static interface MyEntry{//-->鍵值對的映射關係
		
	}
}
class MyDemo implements MyMap.MyEntry{
	
}
     什麼時候使用Map集合呢

    當需求中出現映射關係時,應該最先想到map集合。

    舉例,獲取星期幾,代碼如下:

package ustc.lichunchun.map;

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

import ustc.lichunchun.exception.NoWeekException;

public class MapTest {

	public static void main(String[] args) {
		/*
		 * 什麼時候使用map集合呢?
		 * 當需求中出現映射關係時,應該最先想到map集合。
		 */
		String cnWeek = getCnWeek(3);
		System.out.println(cnWeek);
		String enWeek = getEnWeek(cnWeek);
		System.out.println(enWeek);
	}
	
	/*
	 * 根據中文的星期,獲取對應的英文星期。
	 * 中文與英文相對應,可以建立表,沒有有序的編號,只能通過map集合。
	 */
	public static String getEnWeek(String cnWeek){
		//創建一個表。
		Map<String,String> map = new HashMap<String, String>();
		map.put("星期一","Monday");
		map.put("星期二","Tuesday");
		map.put("星期三","Wednesday");
		map.put("星期四","Thursday");
		map.put("星期五","Friday");
		map.put("星期六","Saturday");
		map.put("星期日","Sunday");
		return map.get(cnWeek);
	}
	
	/*
	 * 根據用戶指定的數據獲取對應的星期。
	 */
	public static String getCnWeek(int num){
		if (num>7 || num<=0)
			throw new NotWeekException(num+", 沒有對應的星期");
		String[] cnWeeks = {"","星期一","星期二","星期三","星期四","星期五","星期六","星期日"};
		return cnWeeks[num];
	}
}

    程序中用到的NotWeekException異常代碼如下:

package ustc.lichunchun.exception;

public class NotWeekException extends RuntimeException {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public NotWeekException() {
		super();
	}

	public NotWeekException(String message, Throwable cause,
			boolean enableSuppression, boolean writableStackTrace) {
		super(message, cause, enableSuppression, writableStackTrace);
	}

	public NotWeekException(String message, Throwable cause) {
		super(message, cause);
	}

	public NotWeekException(String message) {
		super(message);
	}

	public NotWeekException(Throwable cause) {
		super(cause);
	}
	
}

HashMap<K, V>類

    練習1: 員工對象(姓名,年齡)都有對應的歸屬地, 將員工和歸屬存儲到HashMap集合中並取出。同姓名同年齡視爲同一個員工。

package ustc.lichunchun.domain;

public class Employee implements Comparable<Employee>{
	private String name;
	private int age;
	public Employee() {
		super();
	}
	public Employee(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Employee [name=" + name + ", age=" + age + "]";
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Employee other = (Employee) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	@Override
	public int compareTo(Employee o) {
		int temp = this.age-o.age;
		return temp==0?this.name.compareTo(o.name):temp;
	}
}
package ustc.lichunchun.map;

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

import ustc.lichunchun.domain.Employee;

public class HashMapTest {

	public static void main(String[] args) {
		/*
		 * 練習:
		 * 員工對象(姓名,年齡)都有對應的歸屬地。
		 * key=Employee  value=String
		 * 
		 * 1.
		 * 將員工和歸屬存儲到HashMap集合中並取出。
		 * 同姓名同年齡視爲同一個員工。
		 * 
		 */
		Map<Employee,String> map = new HashMap<Employee,String>();//如果改成LinkedHashMap可以實現有序。
		
		map.put(new Employee("xiaozhang",24),"北京");
		map.put(new Employee("laoli",34),"上海");
		map.put(new Employee("mingming",26),"南京");
		map.put(new Employee("xili",30),"廣州");
		map.put(new Employee("laoli",34),"鐵嶺");//上海被覆蓋掉了
		
		for (Employee employee : map.keySet()) {
			System.out.println(employee.getName()+":"+employee.getAge()+"..."+map.get(employee));
		}
	}
}
TreeMap<K, V>類

    練習2:接着上例,按照員工的年齡進行升序排序並取出。-->Comparable。再按照員工的姓名進行升序排序並取出。-->Comparator。

package ustc.lichunchun.map;

import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;

import ustc.lichunchun.domain.Employee;

public class TreeMapTest {
	
	public static void main(String[] args) {
		/*
		 * 練習:
		 * 2.
		 * 按照員工的年齡進行升序排序並取出。-->Comparable
		 * 按照員工的姓名進行升序排序並取出。-->Comparator
		 */
		
		Comparator<Employee> comparator = new Comparator<Employee>(){

			@Override
			public int compare(Employee o1, Employee o2) {
				int temp = o1.getName().compareTo(o2.getName());
				return temp==0?o1.getAge()-o2.getAge():temp;
			}
		};
		
		//Map<Employee,String> map = new TreeMap<Employee,String>();// 按照年齡
		Map<Employee,String> map = new TreeMap<Employee,String>(comparator);//按照姓名
		
		map.put(new Employee("xiaozhang",24),"北京");
		map.put(new Employee("laoli",34),"上海");
		map.put(new Employee("mingming",26),"南京");
		map.put(new Employee("xili",30),"廣州");
		map.put(new Employee("laoli",34),"鐵嶺");
		
		for(Map.Entry<Employee, String> me : map.entrySet()){
			System.out.println(me.getKey().getName()+"::"+me.getKey().getAge()+"..."+me.getValue());
		}
	}
}
     Map查表法練習:

    "bwa-er+b+c=tyx_bac?ecrtdcvr" 獲取字符串中每一個字母出現的次數。要求結果格式:a(2)b(1)d(3)...

    思路:

    1.獲取到字母。
    2.如何獲取字母次數?
        發現字母和次數有對應關係。而且對應關係的一方具備唯一性。就想到了Map集合。map集合就是一張表。
    3.使用查表法就可以了。
        先查第一個字母在表中的次數。如果次數不存在,說明是第一次出現,將該字母和1存儲到表中。以此類推。當要查的次數存在,將次數取出並自增後,再和對應的字母存儲到到表中,map表的特點是相同鍵,值覆蓋!
    4.查完每一個字母后,表中存儲的就是每一個字母出現的次數。

package ustc.lichunchun.test;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class Test {

	public static void main(String[] args) {
		/*
		 * 作業:"bwa-er+b+c=tyx_bac?ecrtdcvr"
		 * 獲取字符串中每一個字母出現的次數。要求結果格式:a(2)b(1)d(3)...
		 * 思路:
		 * 1.獲取到字母。
		 * 2.如何獲取字母次數?
		 * 		發現字母和次數有對應關係。而且對應關係的一方具備唯一性。
		 * 		就想到了Map集合。map集合就是一張表。
		 * 3.使用查表法就可以了。
		 * 		先查第一個字母在表中的次數。如果次數不存在,說明是第一次出現,將該字母和1存儲到表中。
		 * 		以此類推。當要查的次數存在,將次數取出並自增後,再和對應的字母存儲到到表中,map表的特點是相同鍵,值覆蓋!
		 * 4.查完每一個字母后,表中存儲的就是每一個字母出現的次數。
		 */
		String str = "bwa-er+b+c=tyx_bac?ecrtdcvr";
		String char_count = getCharCount(str);
		System.out.println(char_count);
	}

	public static String getCharCount(String str) {
		//1.將字符串轉成字符數組。
		char[] chs = str.toCharArray();
		
		//2.定義map集合表。
		Map<Character, Integer> map = new TreeMap<Character, Integer>();
		
		//3.遍歷字符數組。獲取每一個字母。
		for (int i = 0; i < chs.length; i++) {
			//只對字母操作。
			//if(!(chs[i]>='a'&&chs[i]<='z' || chs[i]>='A'&&chs[i]<='Z'))
			if(!(Character.isLowerCase(chs[i]) || Character.isUpperCase(chs[i])))
				continue;
			//將遍歷到的字母作爲鍵去查表。獲取值。
			Integer value = map.get(chs[i]);
			int count = 0;//用於記錄次數
			//如果次數存在,就用count記錄該次數。如果次數不存在,就不記錄,只對count自增變成1。
			if(value != null){
				count = value;
			}
			count++;
			map.put(chs[i],count);
			/*
			if(value == null){
				map.put(chs[i],1);
			}else{
				value++;
				map.put(chs[i],value);
			}*/
		}
		return mapToString(map);
	}
	/*
	 * 將map集合中的元素轉成指定格式的字符串。a(2)b(1)d(3)......
	 */
	private static String mapToString(Map<Character, Integer> map) {
		//1.數據多,無論類型是什麼,最終都要變成字符串。所以可以使用StringBuilder
		StringBuilder sb = new StringBuilder();
		
		//2.遍歷集合map。keySet()
		for(Character key : map.keySet()){
			Integer value = map.get(key);
			sb.append(key+"("+value+")");
		}
		/*
		Set<Character> keySet = map.keySet();
		for (Iterator<Character> it = keySet.iterator(); it.hasNext();) {
			Character key = it.next();
			Integer value = map.get(key);
			//將鍵值存儲到sb中。
			sb.append(key+"("+value+")");
		}*/
		return sb.toString();
	}
}
集合框架的工具類

Collections類

    java.util.Collections類的出現給集合操作提供了更多的功能。這個類不需要創建對象,完全由在 collection 上進行操作或返回 collection 的靜態方法組成。

    1.對List排序:
         sort(list);//具備泛型限定,保證安全。

    2.逆序:
         reverseOrder
    3.最值:
         max
         min
    4.二分查找:
         binarySearch

    5.將非同步集合轉成同步集合:
         synchronizedCollection
         synchronizedList
         SynchronizedSet
         synchronizedMa

    上述部分功能的解釋

    1.排序方法上的泛型的由來:

interface Comparable<T>
{
	void compareTo(T o);
}

class Student extends Person implements Comparable<Person>
{
	public int compareTo(Person p){//Person可以接受Student類型。
		
	}
}

public static <T extends Comparable<? super T>> void sort(List<T> list)//直接定義泛型T,在編譯時期就保證類型的正確。
{
	//<T extends Comparable>:必須是Comparable的子類才具備比較功能。
	//<? super T>:只要是T的父類,都可以接受T類型,進行比較。
}

public static void sort(List<Object> list)//老版本,即使編譯通過,運行時期不安全,容易出現類型轉換異常。
{
	stu1.compareTo(stu2);
}
    2.模擬一個Collections的min()方法。
/*
 * 模擬一個獲取字符串集合最大值的功能。
 */
public static String getMax(Collection<String> coll){
	Iterator<String> it = coll.iterator();
	
	//定義變量記錄容器中其中一個。
	String max = it.next();
	
	//遍歷容器所有的元素。
	while(it.hasNext()){
		String temp = it.next();
		//在遍歷過程中進行比較。只要比變量中的值大。用變量記錄下來,就哦了。
		if(temp.compareTo(max)>0){
			max = temp;
		}
	}
	return max;
}
/*
 * 模擬一個Collections的min方法。
 */
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll) {
       Iterator<? extends T> i = coll.iterator();
       T candidate = i.next();
        while (i.hasNext()) {
           T next = i.next();
           if (next.compareTo(candidate) < 0)
               candidate = next;
       }
       return candidate;
}
   3.將非同步集合轉成同步集合的原理:定義一個類,將非同步集合所有的方法加同一把鎖後返回。
List list = new ArrayList();//非同步的。

list = MyCollections.synList(list);//返回一個同步的list。

class MyCollections{
	public static List synList(List list){
		return new MyList(list);
	}
	private class MyList{
		private List list;
		private static final Object lock = new Object();
		MyList(List list){
			this.list = list;
		}
		public boolean add(Object obj){
			synchronized(lock){
				return list.add(obj);
			}
		}
		public boolean remove(Object obj){
			synchronized(lock){
				return list.remove(obj);
			}
		}
	}
}

    Collections工具類代碼示例:

package ustc.lichunchun.collections;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import ustc.lichunchun.comparator.ComparatorByLength;

public class CollectionsDemo {

	public static void main(String[] args) {
		/*
		 * Collections排序、逆序、最值、同步方法演示
		 */
		methodDemo1();
		
		Collection<String> coll = new ArrayList<String>();
		coll.add("abcd");
		coll.add("ab");
		coll.add("haha");
		coll.add("zzz");
		String max = getMax(coll);
		String max1 = Collections.max(coll,new ComparatorByLength());
		System.out.println("max = "+max);
		System.out.println("max1 = "+max1);
		
		/*
		 * Collections中有一個可以將非同步集合轉成同步集合的方法。
		 * 同步集合  synchronized集合(非同步集合);
		 */
		Collection<String> synColl = Collections.synchronizedCollection(coll);
	}

	public static void methodDemo1() {
		List<String> list = new ArrayList<String>();
		list.add("abc");
		list.add("xy");
		list.add("haha");
		list.add("nba");
		System.out.println(list);
		
		//對list排序。自然排序。使用的是元素的compareTo方法。
		Collections.sort(list);
		System.out.println(list);
		
		//按照長度排序。
		Collections.sort(list,new ComparatorByLength());
		System.out.println(list);
		
		//按照長度逆序。
		Collections.sort(list,Collections.reverseOrder(new ComparatorByLength()));//reverseOrder強行逆轉比較器順序。
		System.out.println(list);
	}
}
    代碼中用到的比較器實現如下:
package ustc.lichunchun.comparator;

import java.util.Comparator;

public class ComparatorByLength implements Comparator<String> {
	@Override
	public int compare(String o1, String o2) {
		int temp = o1.length()-o2.length();
		return temp==0?o1.compareTo(o2):temp;
	}
}

    Collection 和 Collections的區別

    Collections是個java.util下的類,是針對集合類的一個工具類,提供一系列靜態方法,實現對集合的查找、排序、替換、線程安全化(將非同步的集合轉換成同步的)等操作。

    Collection是個java.util下的接口,它是各種集合結構的父接口,繼承於它的接口主要有Set和List,提供了關於集合的一些操作,如插入、刪除、判斷一個元素是否其成員、遍歷等。

Arrays類

    java.util.Arrays類是用來操作數組的工具類,裏面的方法都是靜態的。代碼示例:

package ustc.lichunchun.arrays;

import java.util.Arrays;

public class ArraysDemo {

	public static void main(String[] args) {
		//int[] arr = new int[3];
		Integer[] arr = new Integer[3];
		String[] arr1 = new String[3];
		swap(arr,1,2);
		swap(arr1,1,2);
		
		int[] arr2 = {45,1,23,56,67};
		System.out.println(arr2);//[I@1db9742
		System.out.println(Arrays.toString(arr2));//[45, 1, 23, 56, 67],底層用的是StringBuilder。
	}
	
	public static <T> void swap(T[] arr, int x, int y){//T必須是引用類型。int--->Integer,自動裝箱拆箱。
		T temp = arr[x];
		arr[x] = arr[y];
		arr[y] = temp;
	}
	
	//toString的源碼實現。
	public static String toString(int[] a) {
        if (a == null)
            return "null";
        int iMax = a.length - 1;
        if (iMax == -1)
            return "[]";

        StringBuilder b = new StringBuilder();
        b.append('[');
        for (int i = 0; ; i++) {//中間省略條件判斷,提高了效率。
            b.append(a[i]);
            if (i == iMax)
                return b.append(']').toString();
            b.append(", ");
        }
    }
}
     Arrays類中有一個很重要的方法就是asList()方法,它返回一個受指定數組支持的固定大小的List列表。所以這裏我就重點說一下,如何實現數組和集合之間的轉換

    1.數組轉成集合

    Arrays.asList方法:將數組轉換成list集合。

<span style="font-size:14px;">String[] arr = {"abc","kk","qq"};
List<String> list = Arrays.asList(arr);//將arr數組轉成list集合。</span>
    將數組轉換成集合,有什麼好處呢?

    用aslist方法,將數組變成集合;可以通過list集合中的方法來操作數組中的元素:isEmpty()、contains、indexOf、set等方法。

    注意(侷限性)

    數組是固定長度,不可以使用集合對象增加或者刪除等,會改變數組長度的功能方法。比如add、remove、clear。(會報不支持操作異常UnsupportedOperationException)。

    如果數組中存儲的引用數據類型,直接作爲集合的元素可以直接用集合方法操作。

    如果數組中存儲的是基本數據類型,asList會將數組實體作爲集合元素存在。

package ustc.lichunchun.arrays;

import java.util.Arrays;
import java.util.List;

public class ArraysDemo2 {

	public static void main(String[] args) {
		/*
		 * 數組轉成集合。
		 * 
		 * Arrays:用來操作數組的工具類,裏面的方法都是靜態的。
		 */
		demo_1();
		demo_2();
	}
	
	public static void demo_1() {
		/*
		 * 重點:Arrays asList(數組) 將數組轉成集合。
		 * 
		 * 爲什麼要把數組轉成集合?
		 * 因爲數組能用的方法是有限的:折半、排序、toString、equals、fill,沒了。
		 * 我想知道數組的位置、是否包含某個元素,這些都沒有,但是集合中有這些方法。
		 * 
		 * 好處:數組轉成List集合,就是爲了使用集合的方法操作數組中的元素。
		 * 
		 * 注意:數組的長度是固定的,所以對於集合的增刪方法是不可以使用的,
		 * 否則會發生UnsupportedOperationException
		 */
		String[] strs = {"abc","haha","nba","zz"};
		
		//以前的做法:自己定義一個函數實現功能
		boolean b = myContains(strs, "nba");
		System.out.println("contains:"+b);

		//現在的做法:數組轉成集合,從而利用集合的方法
		List<String> list = Arrays.asList(strs);
		System.out.println("list contains:"+list.contains("nba"));
		System.out.println(list);
		
		//list.add("qq");//報錯--> java.lang.UnsupportedOperationException
		//因爲數組是固定長度的。
	}
	
	/*
	 * 自定義 對數組中某元素進行查找。
	 */
	public static boolean myContains(String[] arr, String key){
		for (int i = 0; i < arr.length; i++) {
			String str = arr[i];
			if(str.equals(key))
				return true;
		}
		return false;
	}
	
	public static void demo_2() {
		/*
		 * 如果數組中都是引用數據類型,轉成集合時,數組元素直接作爲集合元素。
		 * 如果數組中都是基本數據類型,會將數組對象作爲集合中的元素。(因爲集合中只能存對象)
		 */
		
		int[] arr = {45,23,78,11,99};
		List<int[]> list = Arrays.asList(arr);
		System.out.println(list);//[[I@1db9742]
		System.out.println(list.size());//1
		System.out.println(list.get(0));//[I@1db9742
		//原因:把數組作爲元素,存進了集合中。
		
		//那上面代碼的泛型該咋寫?
		//泛型應該是集合中元素的類型。arr是int[]類型,所以是List<int[]> list
		//實際中,我們應該聲明爲Integer數組,而不是int數組,如下:
		
		Integer[] arr1 = {45,23,78,11,99};
		List<Integer> list1 = Arrays.asList(arr1);
		System.out.println(list1);//[45, 23, 78, 11, 99]
		System.out.println(list1.get(0));//45
	}
}

    2.集合轉成數組

    用的是Collection接口中的方法:toArray()。

    注意

    如果給toArray傳遞的指定類型的數據長度小於了集合的size,那麼toArray方法,會自定再創建一個該類型的數據,長度爲集合的size。
    如果傳遞的指定的類型的數組的長度大於了集合的size,那麼toArray方法,就不會創建新數組,直接使用該數組即可,並將集合中的元素存儲到數組中,其他爲存儲元素的位置默認值null。
    所以,在傳遞指定類型數組時,最好的方式就是指定的長度和size相等的數組。

    將集合變成數組後有什麼好處
    限定了對集合中的元素進行增刪操作,只要獲取這些元素即可。

package ustc.lichunchun.arrays;

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

public class ArraysDemo3 {

	public static void main(String[] args) {
		/*
		 * 集合轉成數組。
		 * 
		 * 使用的就是Collection接口中的toArray方法。
		 * 
		 * 爲什麼要把集合轉成數組?
		 * 可以對集合中的元素操作的方法進行限定,不允許對其進行增刪。
		 */
		List<String> list = new ArrayList<String>();
		list.add("abc");
		list.add("haha");
		
		/*
		 * toArray方法需要傳入一個指定類型的數組。
		 * 長度該如何定義呢?
		 * 傳入的數組長度,如果小於集合長度,那麼該方法會創建一個同類型並和集合相同size的數組。
		 * 傳入的數組長度,如果大於集合長度,那麼該方法會使用指定的數組,存儲集合中的元素,其他位置默認爲null。
		 * 
		 * 所以建議,長度就指定爲集合的size();
		 */
		String[] arr =list.toArray(new String[0]);
		System.out.println(Arrays.toString(arr));//[abc, haha]
		
		String[] arr1 =list.toArray(new String[3]);
		System.out.println(Arrays.toString(arr1));//[abc, haha, null]
		
		String[] arr2 =list.toArray(new String[list.size()]);
		System.out.println(Arrays.toString(arr2));//[abc, haha]
	}
}
可變參數(...)

    JDK1.5出現的技術,用到函數的參數上,當要操作的同一個類型元素個數不確定的時候,可是用這個方式,這個參數可以接受任意個數的同一類型的數據。

    以前我們計算元素之和,用的是下面的方法,針對不同個數,要定義多個函數。

public static int add(int i, int j, int k) {

	return i + j + k;
}

public static int add(int i, int j) {

	return i + j;
}
    之後,我們轉而爲計算數組元素之和,如下所示。
public static int add(int[] arr) {
	int sum = 0;
	for (int i = 0; i < arr.length; i++) {
		sum += arr[i];
	}
	return sum;
}

    但是問題是,數組的長度是固定的,如果不同需求中,個數不一樣,那麼要用不同容量的數組先去存起來,比較麻煩。

    和以前接收數組不一樣的是:以前定義數組類型,需要先創建一個數組對象,再將這個數組對象作爲參數傳遞給函數。

    現在,直接將數組中的元素作爲參數傳遞即可。底層其實是將這些元素進行數組的封裝,而這個封裝動作,是在底層完成的,被隱藏了。所以簡化了用戶的書寫,少了調用者定義數組的動作。

    注意:
    如果在參數列表中使用了可變參數,可變參數必須定義在參數列表結尾(也就是必須是最後一個參數,否則編譯會失敗)。
    如果要獲取多個int數的和呢?可以使用將多個int數封裝到數組中,直接對數組求和即可。

package ustc.lichunchun.param;

public class ParamDemo {

	public static void main(String[] args) {

		int sum = add(4, 5);
		int sum1 = add(4, 5, 6);
		
		/*
		 * 計算多個整數的和。
		 */
		int[] arr = {34,1,4,6};
		int[] arr2 = {23,35,45,6,57,6};
		int sum2 = add(arr);
		int sum3 = add(arr2);
		
		/*
		 * JDK 1.5 可變參數,不用定義數組了。編譯後的class文件中自動帶着數組。
		 * 注意事項:只能定義在參數列表的最後。
		 * public static int add1(int x, int... arr) --> right
		 * public static int add1(int... arr, int x) --> wrong
		 */
		int sum4 = add1(34,1,4,6,57,9);
		int sum5 = add1(99,11,24,56);
	}
	public static int add1(int... arr){
		int sum = 0;
		for (int i = 0; i < arr.length; i++) {
			sum += arr[i];
		}
		return sum;
	}

	public static int add(int[] arr) {
		int sum = 0;
		for (int i = 0; i < arr.length; i++) {
			sum += arr[i];
		}
		return sum;
	}

	public static int add(int i, int j, int k) {

		return i + j + k;
	}

	public static int add(int i, int j) {

		return i + j;
	}
}
靜態導入

    導入了類中的所有靜態成員,簡化靜態成員的書寫。例如:import static java.util.Collections.*; -->導入了Collections類中的所有靜態成員。但如果同名了,還是要加上各自的包名的。

package ustc.lichunchun.staticimport;

import static java.util.Collections.*;

import static java.lang.System.*;

public class StaticImportDemo {

	public static void main(String[] args) {
		/*
		 * 靜態導入。
		 */
		java.util.List<String> list = new java.util.ArrayList<String>();
		list.add("a");
		list.add("c");
		
		sort(list);
		System.out.println(max(list));
		
		out.println("hello");
	}
}

模擬鬥地主

    1.首先模擬一下QQ鬥地主的洗牌和發牌:

package ustc.lichunchun.poker;

import java.util.ArrayList;
import java.util.Collections;

/*
 * 模擬鬥地主洗牌和發牌
 * 
 * 分析:
 * 		A:創建一個牌盒
 * 		B:裝牌
 * 		C:洗牌
 * 		D:發牌
 * 		E:看牌
 */

public class PokerDemo {

	public static void main(String[] args) {
		//創建一個牌盒
		ArrayList<String> array = new ArrayList<String>();
		
		//裝牌
		//黑桃A,黑桃2,黑桃3,...,黑桃k
		//紅桃A,...
		//梅花A,...
		//方塊A,...
		//定義一個花色數組
		String[] colors = {"♠","♥","♣","♦"};
		//定義一個點數數組
		String[] numbers = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
		//裝牌
		for(String color : colors){
			for(String number : numbers){
				array.add(color.concat(number));
			}
		}
		array.add("小王 ");
		array.add("大王 ");
		
		//洗牌
		Collections.shuffle(array);
		
		//發牌
		ArrayList<String> fengQingYang = new ArrayList<String>();
		ArrayList<String> linQingXia = new ArrayList<String>();
		ArrayList<String> liChunChun = new ArrayList<String>();
		ArrayList<String> diPai = new ArrayList<String>();
		
		for (int i = 0; i < array.size(); i++) {
			if(i >= array.size() - 3){
				diPai.add(array.get(i));
			}else if(i % 3 == 0){
				fengQingYang.add(array.get(i));
			}else if(i % 3 == 1){
				linQingXia.add(array.get(i));
			}else{
				liChunChun.add(array.get(i));
			}
		}
		
		//看牌
		lookPoker("風清揚",fengQingYang);
		lookPoker("林青霞",linQingXia);
		lookPoker("李春春",liChunChun);
		
		lookPoker("底牌",diPai);
	}

	public static void lookPoker(String name, ArrayList<String> array) {
		System.out.print(name + "的牌是:");
		for(String s : array){
			System.out.print(s + " ");
		}
		System.out.println();
	}
}
    運行結果如下:


    2.根據上述程序運行結果,我們得考慮發到手裏的牌進行排序,這樣看起來比較舒服。這裏有一個要求,需要儘可能多的使用到本篇博文前面的知識點。所以,做了如下的分析:

    3.針對上述分析,最終實現了模擬鬥地主洗牌發牌、並對手牌進行排序的案例。代碼如下:

package ustc.lichunchun.poker;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;

/*
 * 思路:
 * 		A:創建一個HashMap集合
 * 		B:創建一個ArrayList集合
 * 		C:創建花色數組和點數數組
 * 		D:從0開始往HashMap裏面存儲編號,並存儲對應的牌
 * 	                 同時往ArrayList裏面存儲編號即可。
 * 		E:洗牌(洗的是編號)
 * 		F:發牌(發的也是編號,爲了保證編號是排序的,就創建TreeSet集合接收)
 * 		G:看牌(遍歷TreeSet集合,獲取編號,到HashMap集合找對應的牌)
 */

public class PokerDemo2 {

	public static void main(String[] args) {
		//創建一個HashMap集合
		HashMap<Integer, String> hm = new HashMap<Integer, String>();
		
		//創建一個ArrayList集合
		ArrayList<Integer> array = new ArrayList<Integer>();
		
		//創建花色數組和點數數組
		//定義一個花色數組
		String[] colors = {"♦","♣","♥","♠"};
		//定義一個點數數組
		String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
		
		//從0開始往HashMap裏面存儲編號,並存儲對應的牌,同時往ArrayList裏面存儲編號即可。
		int index = 0;
		for(String number : numbers){
			for(String color : colors){
				String poker = color.concat(number);
				hm.put(index, poker);
				array.add(index);
				index++;
			}
		}
		hm.put(index, "小王");
		array.add(index++);
		hm.put(index, "大王");
		array.add(index);
		
		//洗牌(洗的是編號)
		Collections.shuffle(array);
		
		//發牌(發的也是編號,爲了保證編號是排序的,就創建TreeSet集合接收)
		TreeSet<Integer> fengQingYang = new TreeSet<Integer>();
		TreeSet<Integer> linQingXia = new TreeSet<Integer>();
		TreeSet<Integer> liChunChun = new TreeSet<Integer>();
		TreeSet<Integer> diPai = new TreeSet<Integer>();
		
		for(int i = 0; i < array.size(); i++){
			if(i >= array.size()-3){
				diPai.add(array.get(i));
			}else if(i % 3 == 0){
				fengQingYang.add(array.get(i));
			}else if(i % 3 == 1){
				linQingXia.add(array.get(i));
			}else{
				liChunChun.add(array.get(i));
			}
		}
		
		//看牌(遍歷TreeSet集合,獲取編號,到HashMap集合找對應的牌)
		lookPoker("風清揚", fengQingYang, hm);
		lookPoker("林青霞", linQingXia, hm);
		lookPoker("李春春", liChunChun, hm);
		lookPoker("底牌", diPai, hm);
		
		
	}
	//寫看牌的功能
	public static void lookPoker(String name, TreeSet<Integer> ts, HashMap<Integer, String> hm){
		System.out.print(name + "的牌是:");
		for(Integer key : ts){
			String value = hm.get(key);
			System.out.print(value + " ");
		}
		System.out.println();
	}
}

    程序運行結果如下:


    好了,集合框架的基礎部分學習,我就記錄到這裏。

  如果有疑問,歡迎和我討論,共同進步:[email protected]

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

    另附:Java集合框架面試問題集錦 -- 摘自ImportNew公衆號。

    源碼下載

發佈了50 篇原創文章 · 獲贊 137 · 訪問量 32萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章