zoukankan      html  css  js  c++  java
  • 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);
            
        }
    }
    

  • 相关阅读:
    zookeeper集群搭建
    mysql当前库的查询
    逻辑卷管理
    python小技巧,各种进制的转换
    修剪我的身心
    点击右键很慢,禁用网卡就好了问题解决了(转载)
    实际应用中的C#加密
    ckeditor+ckfinder配置
    ASP.NET MVC学习总结(二)
    MVC3关于用户名的验证
  • 原文地址:https://www.cnblogs.com/lpl666/p/13448167.html
Copyright © 2011-2022 走看看