用斗地主的實例學會使用java Collections工具類

目錄

  • 一、背景
  • 二、概念
    • 1、定義
    • 2、方法
      • 2.1、排序方法
      • 2.2、查找/替換方法
  • 三、斗地主實例
    • 3.1、代碼結構
    • 3.2、常量定義
    • 3.3、單隻牌類
    • 3.4、玩家類
    • 3.5、主程序
  • 四、深入理解

一、背景

最近在學習數據結構和算法的過程中頻繁用到了Collections工具類,這是開發中的一把利器,簡化了許多涉及集合的編碼,該文將通過實例對此工具類進入深入剖析。

二、概念

1、定義

java.util.Collections
是一個包裝類。它包含有各種有關集合操作的靜態多態方法。此類不能實例化,就像一個工具類,服務於Java的集合框架。

public class Collections {
    // 默認構造方法私有化,不允許實例化.
    private Collections() {
    }
    ...
}
2、方法

Collections的方法都為靜態方法,主要分為以下幾類:該文主要對排序查找/替換等方法進行解析。

2.1、排序方法
  • 方法定義:
// 反轉
public static void reverse(List<?> list)
// 隨機排序
public static void shuffle(List<?> list) 
// 按自然排序的升序排序
 public static <T> void sort(List<T> list, Comparator<? super T> c)
// 交換兩個索引位置的元素
public static void swap(List<?> list, int i, int j)
// 旋轉
public static void rotate(List<?> list, int distance)
2.2、查找/替換方法
// 二分查找
int binarySearch(List<? extends Comparable<? super T>> list, T key)
// 根據定製排序,返回最大元素
int max(Collection coll, Comparator c)
// 根據定製排序,返回最小元素
int min(Collection coll, Comparator c)
// 統計元素出現次數
int frequency(Collection c, Object o),統計元素出現次數
// 統計targe在list中第一次出現的索引
int indexOfSubList(List list, List target)
// 用新元素替換舊元素
boolean replaceAll(List list, Object oldVal, Object newVal)

三、斗地主實例

3.1、代碼結構

3.2、常量定義
  • 用集合的方式定義撲克牌的花色、牌面数字、大小王。
/**
 * 撲克牌常量定義
 *  * @author zhuhuix
 * @date 2020-06-05
 */
public class Constant {

    // 紙牌花色:黑桃,紅心,梅花,方塊
    final static List<String> COLORS = new ArrayList<>(
            Arrays.asList(new String[]{"", "", "", ""}));
    // 牌面数字
    final static List<String> NUMBERS = new ArrayList<>(
            Arrays.asList(new String[]{"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"}));
    // 大王小王
    final static List<String> JOKER = new ArrayList<>(
            Arrays.asList(new String[]{"小王","大王"}));
}
3.3、單隻牌類
  • 在單隻牌類的定義中,重寫了會影響到牌面大小的compareTo比較方法:
    — 如果是”“的兩隻牌的比較,則”大王“大於”小王“;
    — 如果是”“與“数字牌”之間的比較,則”“大於“数字牌”;
    — 如果是“数字牌”相互之間的比較,数字大的牌則牌面大,如果数字相同,則按花色比較(<< < )(雖然斗地主不按花色排列大小,但程序會按花色大小進行理牌)。
/**
 * 單隻牌
 *  * @author zhuhuix
 * @date 2020-06-05
 */
public class Card implements Comparable {

    // 花色
    private String color = "";
    //数字
    private String number = "";

    public Card() {
    }

    public Card(String color, String number) {
        this.color = color;
        this.number = number;
    }

    public String getColor() {
        return this.color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getNumber() {
        return this.number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    @Override
    public String toString() {
        return this.color + this.number;
    }

    @Override
    public int compareTo(Object o) {
        if (o instanceof Card) {
            int thisColorIndex = Constant.COLORS.indexOf(this.getColor());
            int anotherColorIndex = Constant.COLORS.indexOf(((Card) o).getColor());
            int thisNumberIndex = Constant.NUMBERS.indexOf(this.getNumber());
            int anotherNumberIndex = Constant.NUMBERS.indexOf(((Card) o).getNumber());

            // 大小王之間相互比較: 大王大於小王
            if ("JOKER".equals(this.color) && "JOKER".equals(((Card) o).getColor())) {
                    return thisColorIndex > anotherColorIndex ? 1 : -1;
            }

            // 大小王與数字牌之間相互比較:大小王大於数字牌
            if ("JOKER".equals(this.color) && !"JOKER".equals(((Card) o).getColor())) {
                return 1;
            }
            if (!"JOKER".equals(this.color) && "JOKER".equals(((Card) o).getColor())) {
                return -1;
            }

            // 数字牌之間相互比較: 数字不相等,数字大則牌面大;数字相等 ,花色大則牌面大
            if (thisNumberIndex == anotherNumberIndex) {
                return thisColorIndex > anotherColorIndex ? 1 : -1;
            } else {
                return thisNumberIndex > anotherNumberIndex ? 1 : -1;
            }

        } else {
            return -1;
        }
    }
}
3.4、玩家類
  • 玩家類中主要定義了抓牌、洗牌、理牌、找牌、獲取最大最小牌、統計炸彈數等成員方法,在這些成員方法中,我們廣泛應用了Collections工具類的靜態方法
/**
 1. 玩家
 2.  * @author zhuhuix
 3. @date 2020-06-05
 */
public class Player {

    // 玩家姓名
    private String name;

    // 玩家類型:农民/地主
    private String type;

    // 抓到的牌
    private List<Card> cards;

    public Player(String name, String type) {
        this.name = name;
        this.type = type;
        this.cards = new ArrayList<>();
    }

    // 洗牌 shuffle
    public void shuffle() {
        Collections.shuffle(this.cards);
    }

    // 理牌 sort
    public void sort() {
        Collections.sort(this.cards, Card::compareTo);
    }

    // 抓牌
    public void draw(Card card) {
        this.cards.add(card);
    }

    // 出牌
    public void play(Card card) {
        this.cards.remove(card);
    }

    // 找出最大牌 max
    public Card max() {
        return Collections.max(this.cards, Card::compareTo);
    }

    // 找出最小牌 min
    public Card min() {
        return Collections.min(this.cards, Card::compareTo);
    }

    // 找到指定牌的位置 binarySearch
    public int binarySearch(Card card) {
        return Collections.binarySearch(this.cards, card, Card::compareTo);
    }

    // 統計有幾手炸彈 frequency
    public int frequency() {
        int count = 0;
        List<String> numbers= new ArrayList<>();
        List<String> colors= new ArrayList<>();
        for (int i = 0; i < this.cards.size(); i++) {
            colors.add(this.cards.get(i).getColor());
            numbers.add(this.cards.get(i).getNumber());
        }
        for (int j = 0; j < Constant.NUMBERS.size(); j++) {
            if (Collections.frequency(numbers, Constant.NUMBERS.get(j)) == 4) {
                count++;
            }
        }
        if (Collections.frequency(colors, "JOKER") == 2) {
            count++;
        }
        return count;
    }

    public String getName() {
        return name;
    }

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

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public List<Card> getCards() {
        return cards;
    }

    public void setCards(List<Card> cards) {
        this.cards = cards;
    }

    @Override
    public String toString() {
        StringBuilder poker = new StringBuilder("[");
        Iterator<Card> iterator = this.cards.iterator();
        while (iterator.hasNext()) {
            poker.append(iterator.next().toString() + ",");
        }
        poker.setCharAt(poker.length() - 1, ']');
        return this.getType() + ":" + this.getName() + "拿到" + this.cards.size() + "張牌:" + poker;
    }
3.5、主程序
  1. 生成一整副牌;
  2. 設置3個玩家,其中兩個為农民,一個為地主;
  3. 3人輪流抓牌,地主多拿三張牌;
  4. 玩家理牌看牌(找到最大牌最小牌);
  5. 判斷大王是否在地主手上?
  6. 統計各個玩家手上有幾個炸彈?
/**
 * 斗地主的主程序
 *
 * @author zhuhuix
 * @date 2020-6-5
 */
public class PlayDemo {

    public static void main(String[] args) {

        // 生成一副撲克牌
        Poker poker = new Poker();
        System.out.println("撲克牌共" + poker.getCardCount() + "只");

        // 設置三個玩家,並設定兩人為农民,一個為地主
        int playCount = 3;
        Player player1 = new Player("玩家1", "农民");
        Player player2 = new Player("玩家2", "农民");
        Player player3 = new Player("玩家3", "地主");

        // 三個玩家按順序抓牌
        int i;
        for (i = 0; i < poker.getCardCount() - playCount; i++) {
            if (i % playCount == 0) {
                player1.draw(poker.getCards().get(i));
            } else if (i % playCount == 1) {
                player2.draw(poker.getCards().get(i));
            } else if (i % playCount == 2) {
                player3.draw(poker.getCards().get(i));
            }
        }

        // 地主拿剩餘底牌
        while (i < poker.getCardCount()) {
            player3.draw(poker.getCards().get(i));
            i++;
        }

        // 展示三個玩家洗牌後手上的牌面及最大和最小的牌
        player1.sort();
        System.out.println(player1.toString());
        System.out.println("最大牌是:"+player1.max().toString()+",最小牌是:"+player1.min().toString());
        player2.sort();
        System.out.println(player2.toString());
        System.out.println("最大牌是:"+player2.max().toString()+",最小牌是:"+player2.min().toString());
        player3.sort();
        System.out.println(player3.toString());
        System.out.println("最大牌是:"+player3.max().toString()+",最小牌是:"+player3.min().toString());

        // 大王是否在地主手裡
        if (player3.binarySearch(new Card("JOKER","大王"))>=0){
            System.out.println("大王在"+player3.getType()+player3.getName()+"手裡");
        }

        // 統計有幾手炸彈
        System.out.println(player1.getName()+"手上有"+player1.frequency()+"手炸彈");
        System.out.println(player2.getName()+"手上有"+player2.frequency()+"手炸彈");
        System.out.println(player3.getName()+"手上有"+player2.frequency()+"手炸彈");
    }
}

程序輸出如下:

四、深入理解

  • 我們對其中的一些方法進行源碼跟蹤
    shuffle
 public static void shuffle(List<?> list, Random rnd) {
        int size = list.size();
        if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
            for (int i=size; i>1; i--)
                swap(list, i-1, rnd.nextInt(i));
        } else {
        	//將集合轉化成數組
            Object arr[] = list.toArray();

            //  通過隨機數隨機交換數組元素位置
            for (int i=size; i>1; i--)
                swap(arr, i-1, rnd.nextInt(i));
			// 通過迭代器將打亂順序的數組賦值給集合
            ListIterator it = list.listIterator();
            for (int i=0; i<arr.length; i++) {
                it.next();
                it.set(arr[i]);
            }
        }
    }

sort

public static <T> void sort(List<T> list, Comparator<? super T> c) {
        list.sort(c);
}
    ...
    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        //調用數組工具類的排序方法--該方法為改進過的歸併排序
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }

min/max

public static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp) {
        if (comp==null)
            return (T)min((Collection) coll);

        Iterator<? extends T> i = coll.iterator();
        T candidate = i.next();
		// 通過迭代器循環比較,找到最小的
        while (i.hasNext()) {
            T next = i.next();
            if (comp.compare(next, candidate) < 0)
                candidate = next;
        }
        return candidate;
 }
 
public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) {
        if (comp==null)
            return (T)max((Collection) coll);

        Iterator<? extends T> i = coll.iterator();
        T candidate = i.next();
		// 通過迭代器循環比較,找到最大的
        while (i.hasNext()) {
            T next = i.next();
            if (comp.compare(next, candidate) > 0)
                candidate = next;
        }
        return candidate;
}

binarySearch

public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c) {
        if (c==null)
            return binarySearch((List<? extends Comparable<? super T>>) list, key);

        if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
            return Collections.indexedBinarySearch(list, key, c);
        else
            return Collections.iteratorBinarySearch(list, key, c);
    }
    ...
    // 二分查找法
    private static <T> int iteratorBinarySearch(List<? extends T> l, T key, Comparator<? super T> c) {
        int low = 0;
        int high = l.size()-1;
        ListIterator<? extends T> i = l.listIterator();

        while (low <= high) {
            int mid = (low + high) >>> 1;
            T midVal = get(i, mid);
            int cmp = c.compare(midVal, key);

            if (cmp < 0)
                low = mid + 1;
            else if (cmp > 0)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found
    }

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

※回頭車貨運收費標準

您可能也會喜歡…