在前面基礎班咱們已經學習過並使用過集合ArrayList<E> ,那麼集合究竟是什麼呢?java
集合和數組既然都是容器,它們有啥區別呢?數組
JAVASE提供了知足各類需求的API,在使用這些API前,先了解其繼承與接口操做架構,才能瞭解什麼時候採用哪一個類,以及類之間如何彼此合做,從而達到靈活應用。架構
集合按照其存儲結構能夠分爲兩大類,分別是單列集合java.util.Collection
和雙列集合java.util.Map
,今天咱們主要學習Collection
集合,在day04時講解Map
集合。框架
java.util.List
和java.util.Set
。其中,List
的特色是元素有序、元素可重複。Set
的特色是元素無序,並且不可重複。List
接口的主要實現類有java.util.ArrayList
和java.util.LinkedList
,Set
接口的主要實現類有java.util.HashSet
和java.util.TreeSet
。從上面的描述能夠看出JDK中提供了豐富的集合類庫,爲了便於初學者進行系統地學習,接下來經過一張圖來描述整個集合類的繼承體系。ide
其中,橙色框裏填寫的都是接口類型,而藍色框裏填寫的都是具體的實現類。這幾天將針對圖中所列舉的集合類進行逐一地講解。工具
集合自己是一個工具,它存放在java.util包中。在Collection
接口定義着單列集合框架中最最共性的內容。學習
Collection是全部單列集合的父接口,所以在Collection中定義了單列集合(List和Set)通用的一些方法,這些方法可用於操做全部的單列集合。方法以下:this
public boolean add(E e)
: 把給定的對象添加到當前集合中 。public void clear()
:清空集合中全部的元素。public boolean remove(E e)
: 把給定的對象在當前集合中刪除。public boolean contains(E e)
: 判斷當前集合中是否包含給定的對象。public boolean isEmpty()
: 判斷當前集合是否爲空。public int size()
: 返回集合中元素的個數。public Object[] toArray()
: 把集合中的元素,存儲到數組中。方法演示:.net
import java.util.ArrayList; import java.util.Collection; public class Demo1Collection { public static void main(String[] args) { // 建立集合對象 // 使用多態形式 Collection<String> coll = new ArrayList<String>(); // 使用方法 // 添加功能 boolean add(String s) coll.add("小李廣"); coll.add("掃地僧"); coll.add("石破天"); System.out.println(coll); // boolean contains(E e) 判斷o是否在集合中存在 System.out.println("判斷 掃地僧 是否在集合中"+coll.contains("掃地僧")); //boolean remove(E e) 刪除在集合中的o元素 System.out.println("刪除石破天:"+coll.remove("石破天")); System.out.println("操做以後集合中元素:"+coll); // size() 集合中有幾個元素 System.out.println("集合中有"+coll.size()+"個元素"); // Object[] toArray()轉換成一個Object數組 Object[] objects = coll.toArray(); // 遍歷數組 for (int i = 0; i < objects.length; i++) { System.out.println(objects[i]); } // void clear() 清空集合 coll.clear(); System.out.println("集合中內容爲:"+coll); // boolean isEmpty() 判斷是否爲空 System.out.println(coll.isEmpty()); } }
tips: 有關Collection中的方法可不止上面這些,其餘方法能夠自行查看API學習。
在程序開發中,常常須要遍歷集合中的全部元素。針對這種需求,JDK專門提供了一個接口java.util.Iterator
。Iterator
接口也是Java集合中的一員,但它與Collection
、Map
接口有所不一樣,Collection
接口與Map
接口主要用於存儲元素,而Iterator
主要用於迭代訪問(即遍歷)Collection
中的元素,所以Iterator
對象也被稱爲迭代器。設計
想要遍歷Collection集合,那麼就要獲取該集合迭代器完成迭代操做,下面介紹一下獲取迭代器的方法:
public Iterator iterator()
: 獲取集合對應的迭代器,用來遍歷集合中的元素的。下面介紹一下迭代的概念:
Iterator接口的經常使用方法以下:
public E next()
:返回迭代的下一個元素。public boolean hasNext()
:若是仍有元素能夠迭代,則返回 true。接下來咱們經過案例學習如何使用Iterator迭代集合中元素:
public class IteratorDemo { public static void main(String[] args) { // 使用多態方式 建立對象 Collection<String> coll = new ArrayList<String>(); // 添加元素到集合 coll.add("串串星人"); coll.add("吐槽星人"); coll.add("汪星人"); //遍歷 //使用迭代器 遍歷 每一個集合對象都有本身的迭代器 Iterator<String> it = coll.iterator(); // 泛型指的是 迭代出 元素的數據類型 while(it.hasNext()){ //判斷是否有迭代元素 String s = it.next();//獲取迭代出的元素 System.out.println(s); } } }
tips::在進行集合元素取出時,若是集合中已經沒有元素了,還繼續使用迭代器的next方法,將會發生java.util.NoSuchElementException沒有集合元素的錯誤。
咱們在以前案例已經完成了Iterator遍歷集合的整個過程。當遍歷集合時,首先經過調用t集合的iterator()方法得到迭代器對象,而後使用hashNext()方法判斷集合中是否存在下一個元素,若是存在,則調用next()方法將元素取出,不然說明已到達了集合末尾,中止遍歷元素。
Iterator迭代器對象在遍歷集合時,內部採用指針的方式來跟蹤集合中的元素,爲了讓初學者能更好地理解迭代器的工做原理,接下來經過一個圖例來演示Iterator對象迭代元素的過程:
在調用Iterator的next方法以前,迭代器的索引位於第一個元素以前,不指向任何元素,當第一次調用迭代器的next方法後,迭代器的索引會向後移動一位,指向第一個元素並將該元素返回,當再次調用next方法時,迭代器的索引會指向第二個元素並將該元素返回,依此類推,直到hasNext方法返回false,表示到達了集合的末尾,終止對元素的遍歷。
加強for循環(也稱for each循環)是JDK1.5之後出來的一個高級for循環,專門用來遍歷數組和集合的。它的內部原理實際上是個Iterator迭代器,因此在遍歷的過程當中,不能對集合中的元素進行增刪操做。
格式:
for(元素的數據類型 變量 : Collection集合or數組){ //寫操做代碼 }
它用於遍歷Collection和數組。一般只進行遍歷元素,不要在遍歷的過程當中對集合元素進行增刪操做。
public class NBForDemo1 { public static void main(String[] args) { int[] arr = {3,5,6,87}; //使用加強for遍歷數組 for(int a : arr){//a表明數組中的每一個元素 System.out.println(a); } } }
public class NBFor { public static void main(String[] args) { Collection<String> coll = new ArrayList<String>(); coll.add("小河神"); coll.add("老河神"); coll.add("神婆"); //使用加強for遍歷 for(String s :coll){//接收變量s表明 表明被遍歷到的集合元素 System.out.println(s); } } }
tips: 新for循環必須有被遍歷的目標。目標只能是Collection或者是數組。新式for僅僅做爲遍歷操做出現。
在前面學習集合時,咱們都知道集合中是能夠存聽任意對象的,只要把對象存儲集合後,那麼這時他們都會被提高成Object類型。當咱們在取出每個對象,而且進行相應的操做,這時必須採用類型轉換。
你們觀察下面代碼:
public class GenericDemo { public static void main(String[] args) { Collection coll = new ArrayList(); coll.add("abc"); coll.add("itcast"); coll.add(5);//因爲集合沒有作任何限定,任何類型均可以給其中存放 Iterator it = coll.iterator(); while(it.hasNext()){ //須要打印每一個字符串的長度,就要把迭代出來的對象轉成String類型 String str = (String) it.next(); System.out.println(str.length()); } } }
程序在運行時發生了問題java.lang.ClassCastException。 爲何會發生類型轉換異常呢? 咱們來分析下:因爲集合中什麼類型的元素均可以存儲。致使取出時強轉引起運行時 ClassCastException。 怎麼來解決這個問題呢? Collection雖然能夠存儲各類對象,但實際上一般Collection只存儲同一類型對象。例如都是存儲字符串對象。所以在JDK5以後,新增了泛型(Generic)語法,讓你在設計API時能夠指定類或方法支持泛型,這樣咱們使用API的時候也變得更爲簡潔,並獲得了編譯時期的語法檢查。
tips:通常在建立對象時,將未知的類型肯定具體的類型。當沒有指定泛型時,默認類型爲Object類型。
上一節只是講解了泛型的引入,那麼泛型帶來了哪些好處呢?
經過咱們以下代碼體驗一下:
public class GenericDemo2 { public static void main(String[] args) { Collection<String> list = new ArrayList<String>(); list.add("abc"); list.add("itcast"); // list.add(5);//當集合明確類型後,存放類型不一致就會編譯報錯 // 集合已經明確具體存放的元素類型,那麼在使用迭代器的時候,迭代器也一樣會知道具體遍歷元素類型 Iterator<String> it = list.iterator(); while(it.hasNext()){ String str = it.next(); //當使用Iterator<String>控制元素類型後,就不須要強轉了。獲取到的元素直接就是String類型 System.out.println(str.length()); } } }
tips:泛型是數據類型的一部分,咱們將類名與泛型合併一塊兒看作數據類型。
咱們在集合中會大量使用到泛型,這裏來完整地學習泛型知識。
泛型,用來靈活地將數據類型應用到不一樣的類、方法、接口當中。將數據類型做爲參數進行傳遞。
泛型是什麼,類變量的類型就是什麼
定義格式:
修飾符 class 類名<表明泛型的變量> { }
例如,API中的ArrayList集合:
class ArrayList<E>{ public boolean add(E e){ } public E get(int index){ } .... }
使用泛型: 即何時肯定泛型。
在建立對象的時候肯定泛型
例如,ArrayList<String> list = new ArrayList<String>();
此時,變量E的值就是String類型,那麼咱們的類型就能夠理解爲:
class ArrayList<String>{ public boolean add(String e){ } public String get(int index){ } ... }
再例如,ArrayList<Integer> list = new ArrayList<Integer>();
此時,變量E的值就是Integer類型,那麼咱們的類型就能夠理解爲:
class ArrayList<Integer> { public boolean add(Integer e) { } public Integer get(int index) { } ... }
舉例自定義泛型類
public class MyGenericClass<MVP> { //沒有MVP類型,在這裏表明 未知的一種數據類型 將來傳遞什麼就是什麼類型 private MVP mvp; public void setMVP(MVP mvp) { this.mvp = mvp; } public MVP getMVP() { return mvp; } }
使用:
public class GenericClassDemo { public static void main(String[] args) { // 建立一個泛型爲String的類 MyGenericClass<String> my = new MyGenericClass<String>(); // 調用setMVP my.setMVP("大鬍子登登"); // 調用getMVP String mvp = my.getMVP(); System.out.println(mvp); //建立一個泛型爲Integer的類 MyGenericClass<Integer> my2 = new MyGenericClass<Integer>(); my2.setMVP(123); Integer mvp2 = my2.getMVP(); } }
泛型是什麼,方法傳遞的參數的類型就是什麼
定義格式:
修飾符 <表明泛型的變量> 返回值類型 方法名(參數){ }
例如,
public class MyGenericMethod { public <MVP> void show(MVP mvp) { System.out.println(mvp.getClass()); } public <MVP> MVP show2(MVP mvp) { return mvp; } }
使用格式:調用方法時,肯定泛型的類型
public class GenericMethodDemo { public static void main(String[] args) { // 建立對象 MyGenericMethod mm = new MyGenericMethod(); // 演示看方法提示 mm.show("aaa"); mm.show(123); mm.show(12.45); } }
定義格式:
修飾符 interface接口名<表明泛型的變量> { }
例如,
public interface MyGenericInterface<E>{ public abstract void add(E e); public abstract E getE(); }
使用格式:
一、定義類時肯定泛型的類型
例如
public class MyImp1 implements MyGenericInterface<String> { @Override public void add(String e) { // 省略... } @Override public String getE() { return null; } }
此時,泛型E的值就是String類型。
二、始終不肯定泛型的類型,直到建立對象時,肯定泛型的類型
例如
public class MyImp2<E> implements MyGenericInterface<E> { @Override public void add(E e) { // 省略... } @Override public E getE() { return null; } }
肯定泛型:
/* * 使用 */ public class GenericInterface { public static void main(String[] args) { MyImp2<String> my = new MyImp2<String>(); my.add("aa"); } }
當使用泛型類或者接口時,傳遞的數據中,泛型類型不肯定,能夠經過通配符<?>表示。可是一旦使用泛型的通配符後,只能使用Object類中的共性方法,集合中元素自身方法沒法使用。
泛型的通配符:不知道使用什麼類型來接收的時候,此時可使用?,?表示未知通配符。
此時只能接受數據,不能往該集合中存儲數據。
舉個例子你們理解使用便可:
public static void main(String[] args) { Collection<Intger> list1 = new ArrayList<Integer>(); getElement(list1); Collection<String> list2 = new ArrayList<String>(); getElement(list2); } public static void getElement(Collection<?> coll){} //?表明能夠接收任意類型
tips:泛型不存在繼承關係 Collection<Object> list = new ArrayList<String>();這種是錯誤的。
以前設置泛型的時候,其實是能夠任意設置的,只要是類就能夠設置。可是在JAVA的泛型中能夠指定一個泛型的上限和下限。
泛型的上限:
類型名稱 <? extends 類 > 對象名稱
只能接收該類型及其子類
泛型的下限:
類型名稱 <? super 類 > 對象名稱
只能接收該類型及其父類型
好比:現已知Object類,String 類,Number類,Integer類,其中Number是Integer的父類
public static void main(String[] args) { Collection<Integer> list1 = new ArrayList<Integer>(); Collection<String> list2 = new ArrayList<String>(); Collection<Number> list3 = new ArrayList<Number>(); Collection<Object> list4 = new ArrayList<Object>(); getElement(list1); getElement(list2);//報錯 getElement(list3); getElement(list4);//報錯 getElement2(list1);//報錯 getElement2(list2);//報錯 getElement2(list3); getElement2(list4); } // 泛型的上限:此時的泛型?,必須是Number類型或者Number類型的子類 public static void getElement1(Collection<? extends Number> coll){} // 泛型的下限:此時的泛型?,必須是Number類型或者Number類型的父類 public static void getElement2(Collection<? super Number> coll){}
按照鬥地主的規則,完成洗牌發牌的動做。
具體規則:
使用54張牌打亂順序,三個玩家參與遊戲,三人交替摸牌,每人17張牌,最後三張留做底牌。
牌能夠設計爲一個ArrayList<String>,每一個字符串爲一張牌。
每張牌由花色數字兩部分組成,咱們可使用花色集合與數字集合嵌套迭代完成每張牌的組裝。
牌由Collections類的shuffle方法進行隨機排序。
將每一個人以及底牌設計爲ArrayList<String>,將最後3張牌直接存放於底牌,剩餘牌經過對3取模依次發牌。
直接打印每一個集合。
import java.util.ArrayList; import java.util.Collections; public class Poker { public static void main(String[] args) { /* * 1: 準備牌操做 */ //1.1 建立牌盒 未來存儲牌面的 ArrayList<String> pokerBox = new ArrayList<String>(); //1.2 建立花色集合 ArrayList<String> colors = new ArrayList<String>(); //1.3 建立數字集合 ArrayList<String> numbers = new ArrayList<String>(); //1.4 分別給花色 以及 數字集合添加元素 colors.add("♥"); colors.add("♦"); colors.add("♠"); colors.add("♣"); for(int i = 2;i<=10;i++){ numbers.add(i+""); } numbers.add("J"); numbers.add("Q"); numbers.add("K"); numbers.add("A"); //1.5 創造牌 拼接牌操做 // 拿出每個花色 而後跟每個數字 進行結合 存儲到牌盒中 for (String color : colors) { //color每個花色 //遍歷數字集合 for(String number : numbers){ //結合 String card = color+number; //存儲到牌盒中 pokerBox.add(card); } } //1.6大王小王 pokerBox.add("小☺"); pokerBox.add("大☠"); // System.out.println(pokerBox); //洗牌 是否是就是將 牌盒中 牌的索引打亂 // Collections類 工具類 都是 靜態方法 // shuffer方法 /* * static void shuffle(List<?> list) * 使用默認隨機源對指定列表進行置換。 */ //2:洗牌 Collections.shuffle(pokerBox); //3 發牌 //3.1 建立 三個 玩家集合 建立一個底牌集合 ArrayList<String> player1 = new ArrayList<String>(); ArrayList<String> player2 = new ArrayList<String>(); ArrayList<String> player3 = new ArrayList<String>(); ArrayList<String> dipai = new ArrayList<String>(); //遍歷 牌盒 必須知道索引 for(int i = 0;i<pokerBox.size();i++){ //獲取 牌面 String card = pokerBox.get(i); //留出三張底牌 存到 底牌集合中 if(i>=51){//存到底牌集合中 dipai.add(card); } else { //玩家1 %3 ==0 if(i%3==0){ player1.add(card); }else if(i%3==1){//玩家2 player2.add(card); }else{//玩家3 player3.add(card); } } } //看看 System.out.println("令狐沖:"+player1); System.out.println("田伯光:"+player2); System.out.println("綠竹翁:"+player3); System.out.println("底牌:"+dipai); } }