1. 程式人生 > 實用技巧 >JavaSE第15篇:集合之Collection集合上篇

JavaSE第15篇:集合之Collection集合上篇

核心概述:在之前的篇章中,我們學習了陣列,因為陣列本身資料結構的侷限性,對於陣列內元素除查詢操作外的其他操作(增刪改)比較低效,所以,我們又學習了集合ArrayList,初步體驗了集合操作的便捷性。本篇我們將開始系統地學習Java中的集合體系。
目錄

第一章:物件陣列

陣列是容器,即可以儲存基本資料型別也可以引用資料型別,儲存了引用資料型別的陣列稱為物件陣列,例如:String[],Person[],Student[]。

public static void main(String[] args){
    //建立儲存Person物件的陣列
    Person[] persons = {
        new Person("張三",20),
        new Person("李四",21),
        new Person("王五",22),
    };
    //遍歷陣列
    for(int i = 0 ; i < persons.length; i++){
    	Person person = persons[i];
    	System.out.println(person.getName()+"::"+person.getAge());
    }
}

陣列的弊端:

  • 陣列長度是固定的,一旦建立不可修改。
  • 需要新增元素,只能建立新的陣列,將原陣列中的元素進行復制。

為了解決陣列的定長問題,Java語言從JDK1.2開始出現集合框架。

第二章:認識集合

1.1-集合概述(瞭解)

在之前的篇章中我們已經學習過並使用過集合ArrayList<E> ,那麼集合到底是什麼呢?

簡而言之,集合就是是java中提供的一種容器,可以用來儲存多個數據。

這麼說,集合和陣列非常相似,就是儲存多個數據的容器。那麼,集合和陣列有什麼區別呢?

  • 陣列的長度是固定的。集合的長度是可變的。
  • 陣列中儲存的是同一型別的元素,可以儲存任意型別資料。集合儲存的都是引用資料型別。如果想儲存基本型別資料需要儲存對應的包裝型別。

1.2-Java中的集合框架(瞭解)

以下的集合體系描述,不是所有的集合,而是常用的集合。

單列集合體系

雙列集合體系

由於集合體系豐富,我們將會分多個篇幅學習,本篇我們將學習單列集合體系Collection中的List系列集合。

1.3-Collection集合通用方法(記憶)

Collection是所有單列集合的父介面,因此在Collection中定義了單列集合(List和Set)通用的一些方法,這些方法可用於操作所有的單列集合。方法如下:

  • public boolean add(E e): 把給定的物件新增到當前集合中 。
  • public boolean addAll(Collection<? extends E>)將另一個集合元素新增到當前集合中。
  • public void clear() :清空集合中所有的元素。
  • public boolean remove(E e): 把給定的物件在當前集合中刪除。
  • public boolean contains(Object obj): 判斷當前集合中是否包含給定的物件。
  • public boolean isEmpty(): 判斷當前集合是否為空。
  • public int size(): 返回集合中元素的個數。
  • public Object[] toArray(): 把集合中的元素,儲存到陣列中。

程式碼示例

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

public class Test {
    public static void main(String[] args) {
        Collection<String> list = new ArrayList<>();
        // 新增元素
        list.add("張三");
        list.add("李四");
        Collection<String> list2 = new ArrayList<>();
        list2.add("王五");
        list2.add("趙六");
        // 將list2集合元素新增到list集合中
        list.addAll(list2);
        System.out.println(list);

        // 移除元素
        list.remove("張三");
        System.out.println(list);
        // 判斷集合中是否包含某個元素
        boolean isHas = list.contains("張三");
        System.out.println(isHas); // false
        // 判斷當前集合是否為空
        boolean isEmpty = list.isEmpty();
        System.out.println(isEmpty);
        // 清空元素
        list.clear();
        System.out.println(list);
        // 集合的長度
        System.out.println(list.size());
        // 集合中的元素儲存到一個數組中
        Object[]s =  list.toArray();
    }
}

第三章:遍歷集合

3.1-Iterator方式遍歷(記憶)

介紹

Iterator,是一個迭代器介面。Collection中的成員方法iterator()被呼叫後,會返回一個Iterator物件。利用這個物件可以實現遍歷集合。如何遍歷呢?在取元素之前先要判斷集合中有沒有元素,如果有,就把這個元素取出來,繼續在判斷,如果還有就再取出出來。一直把集合中的所有元素全部取出。這種取出方式專業術語稱為迭代。

Iterator物件的成員方法:

  • hasNext(); 檢測集合中是否存在下一個元素
  • next(); 找到並獲取下一個元素

示例程式碼:

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

public class Test {
    public static void main(String[] args) {
        Collection<String> list = new ArrayList<>();
        list.add("張三");
        list.add("李四");
        list.add("王五");
        // 得到一個迭代器物件
        Iterator<String> it = list.iterator();
        // 判斷集合中是否還有元素
        while (it.hasNext()) {
            // 取出元素
            String str = it.next();
            System.out.println(str);
        }
    }
}

迭代器執行過程

在呼叫Iterator的next方法之前,迭代器的索引位於第一個元素之前,不指向任何元素,當第一次呼叫迭代器的next方法後,迭代器的索引會向後移動一位,指向第一個元素並將該元素返回,當再次呼叫next方法時,迭代器的索引會指向第二個元素並將該元素返回,依此類推,直到hasNext方法返回false,表示到達了集合的末尾,終止對元素的遍歷。

迭代器原始碼分析

迭代器是遍歷Collection集合的通用方式,任意Collection集合都可以使用迭代器進行遍歷,那麼每一種集合的自身特性是不同的,也就是儲存元素的方式不同,那麼是如何做到遍歷方式的統一呢,接下來我們分析一下迭代器的原始碼。

每個Collection集合都會實現,方法 Iterator iterator(),返回Iterator介面實現類,以ArrayList集合為例

java.util.Iterator介面:

public interface Iterator<E> {
	boolean hasNext();
	E next();
}

java.util.ArrayList類:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
   /*
	*  ArrayList實現介面Collection
    *  重寫方法iterator()
    *  返回Iterator介面實現類 Itr類的物件
	*/
	public Iterator<E> iterator() {
        return new Itr();
    }
    
   /*
    * ArrayList中定義內部類Itr,實現介面Iterator
    * 重寫hasNext(),next()方法
    */
    private class Itr implements Iterator<E> {
    	public boolean hasNext() { 
       		// ...
       }
       public E next() { 
       		// ...
       }
    }

所以結論是:

  • 所有集合的迭代器,全由內部類實現。
  • 集合中定義內部類,實現迭代器介面,可以使所有集合的遍歷方式統一。
  • 呼叫迭代器的方法hasNext(),next()均執行集合中內部類的重寫方法。

併發修改異常

在使用迭代器遍歷集合中,不能使用集合本身的方法改變集合的長度,一旦被改變將會丟擲ConcurrentModificationException併發修改異常。

public static void main(String[] args){
    Collection<String> coll = new ArrayList<String>();
    coll.add("hello1");
    coll.add("hello2");
    coll.add("hello3");
    coll.add("hello4");
    Iterator<String> it = coll.iterator();
    while (it.hasNext()){
      String str = it.next();
      if("hello2".equals(str)){
         coll.add("hello5");
    }
}

以上程式,在迭代器遍歷過程中,使用了集合add方法修改集合的長度,這個操作是不允許的,被禁止的,程式中會丟擲併發修改異常。

3.2-增強for方式遍歷(記憶)

概述

增強for迴圈(也稱for each迴圈)是JDK1.5以後出來的一個高階for迴圈,專門用來遍歷陣列和集合的。它的內部原理其實是個Iterator迭代器,所以在遍歷的過程中,不能對集合中的元素進行增刪操作。

語法格式

for(元素的資料型別  變數 : Collection集合or陣列){ 
  	//寫操作程式碼
}

變數,表示取出的某一個元素

程式碼示例:

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

public class Test {
    public static void main(String[] args) {
        Collection<String> list = new ArrayList<>();
        list.add("張三");
        list.add("李四");
        list.add("王五");
        for (String s : list) {
            System.out.println(s);
        }
    }
}
/*
	輸出結果:
		張三
		李四
		王五
*/

第四章:資料結構

4.1-概述(瞭解)

資料結構就是計算機儲存、組織資料的方式

指的是相互之間存在著特定關係的一種或多種的資料元素集合。

為什麼要學習資料結構呢?

通常情況下,精心選擇合適的資料結構可以帶來更高的執行或儲存的效率

比如:為什麼陣列查詢速度快,增刪改效率較低?為什麼有的集合更適合用於查詢,有的更適合用於增刪改?

4.2-資料結構-棧(瞭解)

介紹

:棧(stack)又名堆疊,是一種運算受限的線性表

受限:限定僅在表尾進行插入和刪除操作的線性表(這一端被稱為棧頂,另一端稱為棧底

這裡兩個名詞需要注意:

  • 壓棧(入棧):就是存元素。即,把元素儲存到棧的頂端位置,棧中已有元素依次向棧底方向移動一個位置。
  • 彈棧(出棧):就是取元素。即,把棧的頂端位置元素取出,棧中已有元素依次向棧頂方向移動一個位置。

特性

先進後出,是棧結構的特點。

4.3-資料結構-佇列(瞭解)

介紹

佇列:是一種受限的特殊線性表。

受限:只允許在表的前端(隊頭)進行刪除操作,後端(隊尾)進行插入操作。

特性

先進先出,是佇列資料結構的特點。

4.4-資料結構-陣列(瞭解)

介紹

陣列:一組有序的(索引有序並且從0開始)型別相同的長度固定的元素集合。

特性

  • 元素有序
  • 元素同類型
  • 長度固定

應用效果

查詢快,從陣列索引0開始查詢,根據指定位置的偏移量可快速獲取資料。

增刪慢,陣列的長度是固定的,若刪除或增加一格元素,則會先建立一個新的陣列,再把原陣列的資料根據操作複製到新陣列中。

4.5-資料結構-連結串列(瞭解)

連結串列

連結串列:linked list,由一系列結點node(連結串列中每一個元素稱為結點)組成,結點可以在執行時動態生成。每個結點包括兩個部分:一個是儲存資料元素的資料域,另一個是儲存下一個結點地址的指標域。我們常說的連結串列結構有單向連結串列與雙向連結串列,那麼這裡給大家介紹的是單向連結串列

特性

  • 多個結點之間,通過地址進行連線。例如,多個人手拉手,每個人使用自己的右手拉住下個人的左手,依次類推,這樣多個人就連在一起了。
  • 結點可以在執行時動態生成。
  • 每個結點包括兩個部分(單鏈表)
    • 一個是儲存資料元素的資料域
    • 另一個是儲存下一個結點地址的指標域。

應用效果

  • 查詢慢:連結串列的地址不是連續的,每次查詢都得從頭開始查詢。
  • 增刪快:增刪操作不會影響連結串列的整體結構。

第五章:List集合

5.1-概述(瞭解)

概述

java.util.List介面,繼承Collection介面,有序的 collection(也稱為序列)。此介面的使用者可以對列表中每個元素的插入位置進行精確地控制。使用者可以根據元素的整數索引(在列表中的位置)訪問元素,並搜尋列表中的元素。與Set介面不同,List介面通常允許重複元素。

特點

  • List集合是有序的集合,儲存和取出的順序一致。
  • List集合允許儲存重複的元素。
  • List集合中的每個元素具有索引。

集合類名字尾是List,例如ArrayList,LinkedList等,都是List介面實現類,都具有List介面的特點。

5.2-List集合常用方法(記憶)

List作為Collection集合的子介面,不但繼承了Collection介面中的全部方法,而且還增加了一些根據元素索引來操 作集合的特有方法,如下:

方法

  1. public void add(int index, E element) : 將指定的元素,新增到該集合中的指定位置上。
  2. public E get(int index) :返回集合中指定位置的元素。
  3. public E remove(int index) : 移除列表中指定位置的元素, 返回的是被移除的元素。
  4. public E set(int index, E element) :用指定元素替換集合中指定位置的元素,返回值的更新前的元素

程式碼:

   List list = new ArrayList();
    list.add("a");
    list.add("b");
    list.add("c");
    // public void add(int index, E element) : 將指定的元素,新增到該集合中的指定位置上。
    list.add(1,"d");
    System.out.println(list); // [a, d, b, c]
    // public E get(int index) :返回集合中指定位置的元素。
    System.out.println(list.get(2)); // b
    // public E remove(int index) : 移除列表中指定位置的元素, 返回的是被移除的元素。
    list.remove(1);
    System.out.println(list); // [a, b, c]
    // public E set(int index, E element) :用指定元素替換集合中指定位置的元素,返回值的更新前的元素
    list.set(1,"B");
    System.out.println(list); // [a, B, c]

第六章:ArrayList集合

6.1-概述(瞭解)

java.util.ArrayList集合資料儲存的結構是陣列結構。元素增刪慢,查詢快,執行緒不安全,執行速度快。由於日常開發中使用最多的功能為查詢資料、遍歷資料,所以ArrayList是最常用的集合。

許多程式設計師開發時非常隨意地使用ArrayList完成任何需求,並不嚴謹,這種用法是不提倡的。

6.2-ArrayList原始碼分析(瞭解)

底層就是陣列

底層是Object物件陣列,陣列儲存的資料型別是Object,陣列名字為elementData。

ArrayList類中部分原始碼

transient Object[] elementData;

建立ArrayList物件分析:無引數

初始化ArrayList物件,建立一個為10的空列表。也可以指定列表長度,構造方法傳遞長度即可。

new ArrayList(); //預設長度為10
new ArrayList(int initialCapacity); //指定長度

ArrayList無引數構造方法分析:

// 定義Object物件型別的空陣列,陣列在記憶體中存在,但長度為0
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 
public ArrayList() {
	this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
 }

解析:這裡可以看出,當我們new ArrayList()的時候,並沒有建立長度為10的陣列,而是建立了一個長度為0的陣列,當我們使用add()方法新增元素的時候,就會將陣列由0長度,擴容為10長度。

ArrayList新增元素add方法分析:

//ArrayList的成員變數size,預設為0,統計集合中元素的個數
private int size;
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  
    elementData[size++] = e;
    return true;
}

解析:集合新增元素之前,先呼叫方法ensureCapacityInternal()增加容量,傳遞size+1,size預設為0。傳遞引數0+1的結果。

ensureCapacityInternal()增加容量方法分析:

private void ensureCapacityInternal(int minCapacity) {
	ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

解析:方法ensureCapacityInternal()接收到引數1,繼續呼叫方法calculateCapacity()計算容量,傳遞陣列和1。

calculateCapacity()計算容量方法分析:

private static final int DEFAULT_CAPACITY = 10;
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

解析:方法中判斷elementData是否和DEFAULTCAPACITY_EMPTY_ELEMENTDATA陣列相等,在構造方法中:this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;因此結果為true,方法將會返回引數DEFAULT_CAPACITY(=10)和 minCapacity(=1)中最大的值,return 10;此時方法calculateCapacity()結束,繼續執行() ensureExplicitCapacity(),傳遞引數10

ensureExplicitCapacity()保證明確容量方法分析:

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

解析:方法中進行判斷(10-elemetData.length>0)結果為true,10-0>0。呼叫方法grow()傳遞10。

grow()增加容量方法分析:

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        //**將10賦值給變數newCapacity
    	newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
    	newCapacity = hugeCapacity(minCapacity);
    //**陣列複製, Arrays.copyOf底層實現是System.arrayCopy()
    elementData = Arrays.copyOf(elementData, newCapacity);
}

解析:方法grow()接收到引數10,進過計算,執行newCapacity = minCapacity;這行程式,此時變數newCapacity的值為10,然後進行陣列複製操作,複製新陣列的長度為10,為此ArrayList集合初始化建立過程完畢。

建立ArrayList物件分析:帶有初始化容量構造方法

ArrayList有引數構造方法分析:new ArrayList(10)

public ArrayList(int initialCapacity) {
	if (initialCapacity > 0) {
		this.elementData = new Object[initialCapacity];
	} else if (initialCapacity == 0) {
		this.elementData = EMPTY_ELEMENTDATA;
	} else {
		throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
	}
}

解析:建立ArrayList集合,傳遞引數10,變數initialCapacity接收到10,直接進行陣列的建立:this.elementData = new Object[initialCapacity]。如果傳遞的引數為0,那麼結果就和使用無引數構造方法相同,如果傳遞的引數小於0,丟擲IllegalArgumentException無效引數異常。

add新增元素方法分析:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

解析:集合新增元素,呼叫方法add並傳遞被新增的元素,首先呼叫方法ensureCapacityInternal()進行容量的檢查,然後將元素新增到elemenetData陣列中,size變數是記錄儲存多少個元素的,預設值為0,新增第一個元素的時候,size為0,新增第一個元素,再++。返回true,List集合允許重複元素。

ensureCapacityInternal()方法最終會執行到grow方法。

private void grow(int minCapacity) {
    //定義變數(老容量),儲存陣列的長度 = 10
    int oldCapacity = elementData.length;
    //定義變數(新容量) = 老容量+老容量右移1位
    //右移是二進位制位計算,相等於除以2,得出新容量=老容量+老容量/2
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
    	newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
    	newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

解析:例如當前的集合中的陣列長度為10,進行擴容。得出新容量+老容量=老容量/2

第七章:LinkedList集合

7.1-概述(瞭解)

java.util.LinkedList集合資料儲存的結構是連結串列結構。方便元素新增、刪除的集合。

集合特點:元素增刪快,查詢慢,執行緒不安全,執行速度快。

LinkedList是一個雙向連結串列,那麼雙向連結串列是什麼樣子的呢,我們用個圖瞭解下:

7.2-特有方法(瞭解)

實際開發中對一個集合元素的新增與刪除經常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。這些方法我們作為瞭解即可

  • public void addFirst(E e):將指定元素插入此列表的開頭。
  • public void addLast(E e):將指定元素新增到此列表的結尾。
  • public E getFirst():返回此列表的第一個元素。
  • public E getLast():返回此列表的最後一個元素。
  • public E removeFirst():移除並返回此列表的第一個元素。
  • public E removeLast():移除並返回此列表的最後一個元素。
  • public E pop():從此列表所表示的堆疊處彈出一個元素。
  • public void push(E e):將元素推入此列表所表示的堆疊。
  • public boolean isEmpty():如果列表不包含元素,則返回true。

LinkedList是List的子類,List中的方法LinkedList都是可以使用,這裡就不做詳細介紹,我們只需要瞭解LinkedList的特有方法即可。在開發時,LinkedList集合也可以作為堆疊,佇列的結構使用。

 	 LinkedList list = new LinkedList();
    list.add("a");
    list.add("b");
    // public void addFirst(E e) :將指定元素插入此列表的開頭。
    list.addFirst("A");
    // public void addLast(E e) :將指定元素新增到此列表的結尾。
    list.addLast("B");
    System.out.println(list); // [A, a, b, B]
    // public E getFirst() :返回此列表的第一個元素。
    System.out.println(list.getFirst()); // A
    // public E getLast() :返回此列表的最後一個元素。
    System.out.println(list.getLast()); // B
    // public E removeFirst() :移除並返回此列表的第一個元素。
    list.removeFirst();
    // public E removeLast() :移除並返回此列表的最後一個元素。
    list.removeLast();
    System.out.println(list); //[a, b]
    // public E pop() :從此列表所表示的堆疊處彈出一個元素。
    list.pop();
    System.out.println(list); // [b]
    // public void push(E e) :將元素推入此列表所表示的堆疊。
    list.push("a");
    System.out.println(list); // [a, b]
    // public boolean isEmpty() :如果列表不包含元素,則返回true。
    System.out.println(list.isEmpty()); // false

7.3-原始碼分析(瞭解)

LinkedList成員變數分析:

public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
	transient int size = 0;
    transient Node<E> first;
    transient Node<E> last;
}

解析:成員變數size是長度,記錄了集合中儲存元素的個數。first和last分別表示連結串列開頭和結尾的元素,因此連結串列可以方便的操作開頭元素和結尾元素。

LinkedList內部類Node類分析:

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

	Node(Node<E> prev, E element, Node<E> next) {
		this.item = element;
		this.next = next;
		this.prev = prev;
	}
}

解析:LinkedList集合中的內部類Node,表示連結串列中的節點物件,Node類具有3個成員變數:

  • item:儲存的物件。
  • next:下一個節點。
  • prev:上一個節點。

從Node類的原始碼中可以分析出,LinkedList是雙向連結串列,一個物件,他記錄了上一個節點,也記錄了下一個節點。

LinkedList新增元素方法add()分析:

public boolean add(E e) {
	linkLast(e);
	return true;
}

linkLast方法

void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
    	first = newNode;
    else
    	l.next = newNode;
    size++;
    modCount++;
}

解析:呼叫集合方法add()新增元素,本質上呼叫的是linkLast()方法進行新增。

  • final Node<E> l = last:當集合中新增第一個元素時last=null。
  • final Node<E> newNode = new Node<>(l, e, null):建立Node類內部類物件,傳遞null(上一個節點),被新增的元素和null(下一個節點)。
  • last = newNode:將新增的節點newNode,複製給連結串列中的最後一個節點last。
  • if(l == null):第一次新增元素時,結果為true,first=newNode,連結串列中的第一個節點=新新增的節點。
  • size++ :記錄了集合中元素的個數。
  • modCount++:記錄了集合被操作的次數。

LinkedList獲取元素方法get()分析

public E get(int index) {
	checkElementIndex(index);
	return node(index).item;
}

node方法

Node<E> node(int index) {
    if (index < (size >> 1)) {
    Node<E> x = first;
    for (int i = 0; i < index; i++)
    	x = x.next;
    	return x;
    } else {
    Node<E> x = last;
    for (int i = size - 1; i > index; i--)
    	x = x.prev;
    return x;
	}
}

解析:index < (size >> 1)採用二分法,如果要獲取元素的索引小於長度的一半,那麼就從0開始,找到集合長度的一半,如果要獲取的元素的長度大於集合的一半,那麼就從最大索引開始,找到集合長度的一半。

結論:連結串列本身並沒有索引,當我們通過索引獲取的時候,內部採用了迴圈到集合長度的方式依次查詢的。

第八章:綜合案例

8.1-需求

按照鬥地主的規則,完成洗牌發牌的動作。
具體規則:

  • 使用54張牌打亂順序,
  • 三個玩家參與遊戲,
  • 三人交替摸牌,每人17張牌,
  • 最後三張留作底牌。

8.2-分析

牌可以設計為一個ArrayList<String>,每個字串為一張牌。
每張牌由花色數字兩部分組成,我們可以使用花色集合與數字集合巢狀迭代完成每張牌的組裝。
牌由Collections類的shuffle方法進行隨機排序。

8.3-程式碼

package www.penglei666.com;

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 (int i =0 ; i<colors.size() ;i++) {
            //color每一個花色 guilian
            //遍歷數字集合
            for(int j = 0; j <numbers.size() ; j++){
                //結合
                String card = colors.get(i)+numbers.get(j);
                //儲存到牌盒中
                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);
        
    }
}