zoukankan      html  css  js  c++  java
  • 【转】 【JavaSE】集合容器的总结(全)

    【转】 【JavaSE】集合容器的总结(全)

    @

    总体框架图:
    在这里插入图片描述
    在这里插入图片描述

    一、Collection接口

    基本功能

    在这里插入图片描述
    遍历
    方式一:集合转数组
    在这里插入图片描述

    方式二:使用集合自带的迭代器

    • iterator 集合的专有遍历方式,通过集合的iterator()方法获得,所以迭代器是依赖于集合而存在的
    //得到迭代器来遍历数组。迭代器与数组是相辅相成的
    Iterator it =c.iterator();//通过集合的方法返回迭代器对象
    while (it.hasNext()){
       String s= (String) it.next();
        System.out.println(s);
    }
    

    注意基础迭代器会存在并发修改异常的问题,这个后面再详细分析源码讨论

    在这里插入图片描述

    1. List

    List 集合概述

    • 有序集合(也称序列),用户可以精确控制列表中的每个元素的插入位置,通过整数索引访问元素
    • 与 Set集合不同,List允许有重复元素
      特点
    • 有序:存储与取出的元素顺序一致
    • 可重复:存储的元素可重复

    List集合的特有方法
    在这里插入图片描述
    遍历(没有学泛型之前,都要强转)

    • Iterator 普通迭代器在一边迭代同时又出现修改集合操作的时候,会发生异常

    • ListIterator listIterator()(继承Iterator,可以从后往前遍历,但开发中不怎么用它)遍历List集合时修改元素不会发生并发修改异常

    出现的原因
    普通迭代器在迭代期间不允许修改元素
    迭代器遍历的过程中,通过集合对象修改了集合中的元素,造成了迭代器获取元素中判断预期修改值和实际修改值不一致,则会出现:ConcurrentModificationException
    解决的方案
    普通 for循环遍历,然后用集合对象做对应的操作即可,切记不可以用 增强 for 循环
    或者使用专有迭代器 ListIterator

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    列表迭代器【应用】
    ListIterator 介绍

    • 通过 List集合的 listIterator()方法得到,所以说它是List集合特有的迭代器
    • 用于允许程序员沿任一方向遍历的列表迭代器,从后往前遍历也可以实现
    • 允许在迭代期间修改列表,并获取列表中迭代器的当前位置

    在这里插入图片描述
    增强for循环【应用】

    for(String s : list) {
          System.out.println(s);
       }
       
       //内部原理是一个Iterator迭代器
        
        for(String s : list) {
          if(s.equals("world")) {
            list.add("javaee"); //ConcurrentModificationException
          //和普通迭代器一样会抛出并发修改异常
          }
        }
        
    

    List集合子类的特点【记忆】

    • ArrayList::底层是数组结构实现,查询快、增删慢
    • LinkedList: 底层是链表结构实现,查询慢、增删快
    • Vector:底层数据结构是数组。线程安全

    ArrayList

    参考:https://blog.csdn.net/weixin_40304387/article/details/80790177
    https://www.cnblogs.com/V1haoge/p/10414458.html

    1. 概述
    ArrayList底层使用的是数组。是List的可变数组实现,这里的可变是针对List而言,而不是底层数组。
    数组有自身的特点,不变性,一旦数组被初始化,那么其长度就固定了,不可被改变。这就导致了ArrayList中的一个重要特性:扩容

    public static void main(String[] args) {
        ArrayList<String> arr=new ArrayList<>();
        arr.add("hello");
        arr.add("world");
        arr.add("java");
        //arr.add(3,"zy");
        arr.add(4,"zy");//IndexOutOfBoundsException: Index: 4, Size: 3
        System.out.println(arr);
    }
    

    案例:元素去重

    /**
     * 字符串去重:与自身比较
     */
    public class ArrayListDemo01 {
        public static void main(String[] args) {
            ArrayList arrayList = new ArrayList();
            arrayList.add("hello");
            arrayList.add("java");
            arrayList.add("hello");
            arrayList.add("world");
    
            for (int i = 0; i < arrayList.size() - 1; i++){
                for (int j = i + 1; j<arrayList.size();j++){
                    if (arrayList.get(i).equals(arrayList.get(j))){
                        arrayList.remove(j);
                        j--;
                    }
                }
            }
            //遍历集合
            for (int i = 0; i < arrayList.size(); i++){
                System.out.println(arrayList.get(i));
            }
        }
    }
    
    
    /**
     * 自定义对象的集合,去重
     * 自定义对象一定要重写 equals方法
     */
    
    public class ArrayListDemo02 {
        public static void main(String[] args) {
    
            Student stu1 = new Student("张三",18);
            Student stu2 = new Student("李四",20);
            Student stu3 = new Student("张三",18);
            Student stu4 = new Student("张三",18);
            Student stu5 = new Student("张三",18);
    
            ArrayList list = new ArrayList();
            list.add(stu1);
            list.add(stu2);
            list.add(stu3);
            list.add(stu4);
            list.add(stu5);
            ArrayList newList = new ArrayList();
            // 元素去重
            for (int i = 0; i < list.size(); i++ ){
                Student  s = (Student) list.get(i);//没有使用泛型,这里需要强转一下
                if (!newList.contains(s)){
                    newList.add(s);
                }
            }
    
            //遍历newList
            for (int i = 0; i < newList.size(); i++){
                Student s = (Student) newList.get(i);
                System.out.println(s);
            }
    
        }
    }
    
    

    contains() 方法的源码分析:为什么要重写equals()方法
    不重写 equals() 方法的话,那list 集合中的每个对象在比较时,内存地址都不一样都是一个完全新生的不同的对象,那么 比较内容 也就不奏效了。
    在这里插入图片描述

    思考:去除字符串的两种方法,一种用了contains,另一种用了equals。其实去除自定义对象时也可以直接用equals,这样更明确需要重写equals。

    contains()底层依赖于equals(),String已经重写,所以我们也要把自定义对象重写(eclipse自动生成,但最好自己知道怎么写!)

    LinkedList

    LinkedList集合的特有功能【应用】
    在这里插入图片描述

    使用 LinkedList 模拟栈

    public class MyStack {
    	private LinkedList link;
    
    	public MyStack() {
    		link = new LinkedList();
    	}
    
    	public void add(Object obj) {
    		link.addFirst(obj);
    	}
    
    	public Object get() {
    		// return link.getFirst();
    		return link.removeFirst();
    	}
    
    	public boolean isEmpty() {
    		return link.isEmpty();
    	}
    }
    
    

    Vector

    底层是数组,线程安全,效率低,查询快,增删慢
    在这里插入图片描述

    Vector的两种遍历方式
    在这里插入图片描述

    CopyOnWriteArrayList

    参考: CopyOnWriteArrayList,冷门容器却每次面试都问

    1. CopyOnWriteArrayList的出现原因

    一些案例说明了ArrayList使用的局限性,既然是非线程安全,会出现并发修改异常问题,也就是读写时的加锁问题。

    那我们就使用一些机制把它变安全不就好了。变安全的方法有很多。比如说替换成Vector,再或者是使用 Collections,可以将 ArrayList 包装成一个线程安全的类。不过这两种方法也有很大的缺点,那就是他们使用的都是独占锁,独占式锁在同一时刻只有一个线程能够获取,效率太低。于是CopyOnWriteArrayList 应用而生了。

    2、CopyOnWriteArrayList 介绍

    (1)独占锁效率低:采用读写分离思想解决

    既然独占锁的效率低下,那我们可以换一种方式,采用读写分离式的思想将读操作和写操作进行分开即可。

    读操作不加锁,所有线程都不会阻塞。写操作加锁,线程会阻塞。

    (2)写线程获取到锁,其他线程包括读线程阻塞

    但是这时候又出现了另外一个问题了:写线程获取到锁之后,其他的读线程会陷入阻塞。

    (3)复制思想:解决问题2

    这咋办呢?我们可以再转化一下思想:

    当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行 Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。

    这时候会抛出来一个新的问题,也就是数据不一致的问题。如果写线程还没来得及写会内存,其他的线程就会读到了脏数据。

    这就是CopyOnWriteArrayList 的思想和原理。就是拷贝一份写。所以使用条件也很局限,那就是在读多写少的情况下比较好。

    3、源码分析(基于JDK1.8)

    • 读取
    public class CopyOnWriteArrayList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
        
        private transient volatile Object[] array;
     
        final Object[] getArray() {
            return array;
        }
        
        public E get(int index) {
            return get(getArray(), index);
        }
     
        
        private E get(Object[] a, int index) {
            return (E) a[index];
        }
    

    可以看到,读取代码没有任何同步控制和锁操作,因为内部数组array不会发生修改,只会被另一个array替换,可以保证数据安全。

    • 写操作
    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }
    

    可以看到,写操作使用了锁 ReentrantLock
    重点在 Object[] newElements = Arrays.copyOf(elements, len + 1);这里在生成一个新的数组,然后将新的元素加入到newElements中,
    再将新的数组替换成老的数组,修改就完成了, setArray(newElements);

    整个过程不会影响到读取的线程。当修改完成后,读取线程可以立即察觉到这个修改,因为array被volatile修饰了。

    4、总结

    这个容器很简单,虽然是采用了读写分离的思想,但是却有很大不同,不同之处在于copy。

    1、读写锁

    读线程具有实时性,写线程会阻塞。解决了数据不一致的问题。但是读写锁依然会出现读线程阻塞等待的情况

    2、CopyOnWriteArrayList

    读线程具有实时性,写线程会阻塞。不能解决数据不一致的问题。但是CopyOnWriteArrayList 不会出现读线程阻塞等待的情况

    在这里插入图片描述

    2. Set

    Set集合概述和特点

    • 无序:元素存储和取出没有顺序
    • 遍历:没有索引、只能通过迭代器 iterator 或增强 for循环遍历
    • 不重复:不能存储重复元素

    Set没有特有功能方法,都和 Collection 一样

    //创建集合对象
        Set<String> set = new HashSet<String>();
    

    HashSet

    HashSet 集合的特点

    • 底层数据结构是哈希表
    • 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
    • 没有带索引的方法,所以不能使用普通 for循环遍历
    • 由于是 Set集合,所以是不包含重复元素的集合
    • HashSet底层数据结构是哈希表:是一个元素为链表的数组,综合了数组和链表的好处。

    HashSet 集合的基本使用

    HashSet<String> hs = new HashSet<String>();
    

    HashSet集合保证元素唯一性源码分析

    1.根据对象的哈希值计算存储位置

    • 如果当前位置没有元素则直接存入
    • 如果当前位置有元素存在,则进入第二步

    .2.当前元素的元素和已经存在的元素比较哈希值

    • 如果哈希值不同,则将当前元素进行存储
    • 如果哈希值相同,则进入第三步

    3.通过equals()方法比较两个元素的内容

    • 如果内容不相同,则将当前元素进行存储
    • 如果内容相同,则不存储当前元素

    在这里插入图片描述

    LinkedHashSet

    LinkedHashSet 集合特点

    • 哈希表和链表实现的 Set接口,具有可预测的迭代次序
    • 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
    • 由哈希表保证元素唯一,也就是说没有重复的元素

    TreeSet

    TreeSet 集合特点

    • 元素有序,可以按照一定的规则进行排序,具体排序方式取决于构造方法

    • TreeSet() :根据其元素的自然排序进行排序

    • TreeSet(Comparator comparator) :根据指定的比较器进行排序

    • 没有带索引的方法,所以不能使用普通 for循环遍历

    • 由于是 Set集合,所以不包含重复元素的集合

    TreeSet的add()源码解析

    底层是二叉树结构

    • .第一个元素作为根节点,和后面的元素比较,小的放左边,大的放右边,相同的不放
    • 在二叉树结构下,排序又依赖于comparable接口的compareTo方法,按照指定规则强行给对象比大小,然后存入树中。

    在这里插入图片描述
    Integer自然排序演示

    Integer已经实现comparable接口并重写compareTo()方法
    另外,String也重写了compareTo()方法,根据字典顺序排序字符串

    public class TreeSetDemo01 {
      public static void main(String[] args) {
        //创建集合对象
        TreeSet<Integer> ts = new TreeSet<Integer>();//默认无参构造方法,自然排序
        //添加元素
        ts.add(10);
        ts.add(40);
        ts.add(30);
        ts.add(50);
        ts.add(20);
        ts.add(30);
        //遍历集合
        for(Integer i : ts) {
          System.out.println(i);//10 20 30 40 50 
       }
     }
    }
    

    存储自定义对象并遍历练习
    Student类实现comparable接口并重写compareTo()方法

    @Override
        public int compareTo(Student s) {
    
            int num = this.age - s.age;
            int num2 = num == 0 ? this.name.compareTo(s.name) : num;
            return num2;
        }
    

    在这里插入图片描述

    自定义排序比较器
    TreeSet(Comparator<? super E> comparator):比较器排序(集合具备比较性)

    方式一:创建Mycomparator类实现comparator接口,重写compare()方法:比较条件和自然排序一样写

    方式二:如果只用一次,不另外创建比较器,直接用匿名对象传参(开发常用)

    在这里插入图片描述
    在这里插入图片描述

    总结 TreeSet集合
    在这里插入图片描述

    二、Map接口

    Map 集合的特点
    MAP的键要保证唯一性,所以在用自定义的类做键时,一定要重写两个方法:hashmap equals方法

    双列集合接口

    interface Map<K,V>  K:键的类型;V:值的类型 
    
    • 键值对映射关系
    • 一个键对应一个值
    • 键不能重复,值可以重复
    • 元素存取无序

    在这里插入图片描述
    Map集合的遍历(方式1)

    遍历思路

    • 我们刚才存储的元素都是成对出现的,所以我们把 Map看成是一个夫妻对的集合
    • 把所有的丈夫给集中起来
    • 遍历丈夫的集合,获取到每一个丈夫
    • 根据丈夫去找对应的妻子
    //获取所有键的集合。用keySet()方法实现
        Set<String> keySet = map.keySet();
        //遍历键的集合,获取到每一个键。用增强for实现
        for (String key : keySet) {
          //根据键去找值。用get(Object key)方法实现
          String value = map.get(key);
          System.out.println(key + "," + value);
       }
     }
    

    Map集合的遍历(方式2)
    步骤分析

    • 获取所有键值对对象的集合
    • Set<Map.Entry<K,V>> entrySet() :获取所有键值对实体的集合
    • 遍历键值对实体的集合,得到每一个键值对对象
    • 用增强 for实现,得到每一个Map.Entry
    • 根据键值对对象获取键和值
      • 用 getKey()得到键
      • 用 getValue()得到值
    //创建集合对象
        Map<String, String> map = new HashMap<String, String>();
    
    //获取所有键值对对象的集合
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        //遍历键值对对象的集合,得到每一个键值对对象
        for (Map.Entry<String, String> me : entrySet) {
          //根据键值对对象获取键和值
          String key = me.getKey();
          String value = me.getValue();
          System.out.println(key + "," + value);
       }
    

    在这里插入图片描述

    HashMap

    HashMap是基于哈希表的Map实现,此处的哈希表结构是用来保证键的唯一性的。

    HashMap的几个案例(存储不同的键和值类型)

    • HashMap<String, String>
    • HashMap<Integer, String>
    • HashMap<String, Student>
    • HashMap<Student, String>

    几个案例中,最后一个案例最重要:键为student对象,值为String。所谓“键相同,值覆盖”的特性,底层必须借助哈希表保证键的唯一性,也就是student元素的唯一性,由于HashMap键的唯一性是依赖于哈希表,哈希表依赖于hashCode()和equals()的重写。student要保证作为键的唯一性,则必须重写这两个方法!

    练习:集合嵌套之ArrayList嵌套HashMap

    //创建ArrayList集合
        ArrayList<HashMap<String, String>> array = new
    ArrayList<HashMap<String, String>>();
    
     //创建HashMap集合,并添加键值对元素
        HashMap<String, String> hm1 = new HashMap<String, String>();
        hm1.put("孙策", "大乔");
        hm1.put("周瑜", "小乔");
        
     HashMap<String, String> hm2 = new HashMap<String, String>();
        hm2.put("郭靖", "黄蓉");
        hm2.put("杨过", "小龙女"); 
          
      HashMap<String, String> hm3 = new HashMap<String, String>();
        hm3.put("令狐冲", "任盈盈");
        hm3.put("林平之", "岳灵珊");  
        
        /把HashMap作为元素添加到ArrayList集合
        array.add(hm1);
        array.add(hm2);
        array.add(hm3);
        
       //遍历ArrayList集合
        for (HashMap<String, String> hm : array) {
          Set<String> keySet = hm.keySet();
          for (String key : keySet) {
            String value = hm.get(key);
            System.out.println(key + "," + value);
         } 
        
    

    练习:集合嵌套之HashMap嵌套ArrayList

    //创建HashMap集合
        HashMap<String, ArrayList<String>> hm = new HashMap<String,
    ArrayList<String>>();
    //创建ArrayList集合,并添加元素
        ArrayList<String> sgyy = new ArrayList<String>();
        sgyy.add("诸葛亮");
        sgyy.add("赵云");
    
    ArrayList<String> xyj = new ArrayList<String>();
        xyj.add("唐僧");
        xyj.add("孙悟空");
        
         ArrayList<String> shz = new ArrayList<String>();
        shz.add("武松");
        shz.add("鲁智深");
        
        
        //把ArrayList作为元素添加到HashMap集合
        hm.put("三国演义",sgyy);
         hm.put("西游记",xyj);
         hm.put("水浒传",shz);
         
         
          //遍历HashMap集合
        Set<String> keySet = hm.keySet();
        for(String key : keySet) {
          System.out.println(key);
          ArrayList<String> value = hm.get(key);
          for(String s : value) {
            System.out.println("	" + s);
         }
       }
    

    题型:统计字符串中每个字符出现的次数
    案例需求

    • 键盘录入一个字符串,要求统计字符串中每个字符串出现的次数。
    • 举例:键盘录入 “aababcabcdabcde” 在控制台输出:“a(5)b(4)c(3)d(2)e(1)”
      代码实现
    public class CharCountDemo {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入字符串: ");
            String line = sc.nextLine();
    
            //创建一个map集合,键是Character,值是Integer(HashMap,TreeMap类型都可)
            HashMap<Character,Integer> map = new HashMap<Character,Integer>();
           // TreeMap<Character,Integer> map = new TreeMap<>();
            
            //遍历字符串的每一个字符,判断该字符在map中是否存在该键
            for (int i = 0; i < line.length(); i++){
                char key = line.charAt(i);
                Integer value = map.get(key);
                //如果返回值是null:说明该字符在Map集合中不存在,就把该字符作为键, 1 作为值存储
                if (value == null){
                    map.put(key,1);
                }else{
                    //如果返回值不是null:说明该字符在Map集合中存在,把该值加1,然后重新存储该字符和对应的值
                    value++;
                    map.put(key,value);
                }
            }
    
            //遍历map集合
            StringBuilder sb = new StringBuilder();
            Set<Character> keys = map.keySet();
            for (Character key : keys){
                Integer value = map.get(key);
                sb.append(key).append("(").append(value).append(")");
            }
            System.out.println(sb.toString());
        }
    }
    
    

    LinkedHashMap

    Map接口的哈希表和链表列表实现,具有可预知的迭代顺序

    • 哈希表保证唯一性
    • 链表保证有序性

    Hashtable

    (和HashMap几乎一样,被HashMap替代了)

    Hashtable与HashMap的区别

    • HashMap:线程不安全,效率高,允许null键和null值
    • Hashtable:线程安全,效率低,不允许null键和null值

    TreeMap

    参考:https://www.jianshu.com/p/e11fe1760a3d
    概述:

    • TreeMap存储K-V键值对,通过红黑树(R-B tree)实现;
    • TreeMap继承了NavigableMap接口,NavigableMap接口继承了SortedMap接口,可支持一系列的导航定位以及导航操作的方法,当然只是提供了接口,需要TreeMap自己去实现;
    • TreeMap实现了Cloneable接口,可被克隆,实现了Serializable接口,可序列化;
    • TreeMap因为是通过红黑树实现,红黑树结构天然支持排序,默认情况下通过Key值的自然顺序进行排序;

    三种Map子类集合的对比:

    • HashMap可实现快速存储和检索,但其缺点是其包含的元素是无序的,这导致它在存在大量迭代的情况下表现不佳。
    • LinkedHashMap保留了HashMap的优势,且其包含的元素是有序的。它在有大量迭代的情况下表现更好。
    • TreeMap能便捷的实现对其内部元素的各种排序,但其一般性能比前两种map差。

    LinkedHashMap映射减少了HashMap排序中的混乱,且不会导致TreeMap的性能损失。


    ConcurrentHashMap

    链接:https://www.jianshu.com/p/d0b37b927c48
    HashMap线程不安全
    因为多线程环境下,使用Hashmap进行put操作可能会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap。

    Hashtable线程安全但效率低下
    Hashtable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下Hashtable的效率非常低下。因为当一个线程访问Hashtable的同步方法时,其他线程访问Hashtable的同步方法时,可能会进入阻塞或轮询状态。如线程1使用put进行添加元素,线程2不但不能使用put方法添加元素,并且也不能使用get方法来获取元素,所以竞争越激烈效率越低。

    Hashtable
    的任何操作都会把整个表锁住,是阻塞的。好处是总能获取最实时的更新,比如说线程A调用putAll写入大量数据,期间线程B调用get,线程B就会被阻塞,直到线程A完成putAll,因此线程B肯定能获取到线程A写入的完整数据。坏处是所有调用都要排队,效率较低。


    ConcurrentHashMap分段锁
    是设计为非阻塞的。在更新时会局部锁住某部分数据,但不会把整个表都锁住。同步读取操作则是完全非阻塞的。好处是在保证合理的同步前提下,效率很高。坏处是严格来说读取操作不能保证反映最近的更新。例如线程A调用putAll写入大量数据,期间线程B调用get,则只能get到目前为止已经顺利插入的部分数据。
    应该根据具体的应用场景选择合适的HashMap。


    三、Collections 集合工具类

    方法名 说明

    • 排序:public static <T> void sort(List<T> list)
      默认情况下是自然顺序,所以自定义对象要实现Comparable接口

    • 反转 : public static void reverse(List<?> list) 反转指定列表中元素的顺序

    • 随机置换 : public static void shuffle(List<?> list) 使用默认的随机源随机排列指定的列表

    • 二分查找:public static <T> int binarySearch(List<?> list, T key)

    • 最大值:public static <T> max(Collection<?> coll)

    案例:使用工具类对arraylist进行排序

    
    {
    
        ArrayList<Student> array = new ArrayList<Student>();
    
        //创建学生对象
        Student s1 = new Student("lin", 30);
        Student s2 = new Student("wang", 32);
        Student s3 = new Student("liu", 33);
        Student s4 = new Student("zhang", 33);
    
        //把学生对象添加到集合
        array.add(s1);
        array.add(s2);
        array.add(s3);
        array.add(s4);
    
        //使用Collections 对ArrayList集合进行排序
        Collections.sort(array, new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                //先按年龄排序,后按名字排序
                int num = s1.getAge() - s2.getAge();
                int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
                return num2;
            }
        });
        //遍历集合
        for (Student s : array) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
    

    案例:斗地主

    
    public class PokerDemo {
        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){
                    hm.put(index,color+number);
                    array.add(index);
                    index++;
                }
            }
            hm.put(index,"小王");
            array.add(index);
            index++;
            hm.put(index,"大王");
            array.add(index);
            
            //洗牌(洗的是编号),用Collections的shuffle()方法实现
            Collections.shuffle(array);
    
            //发牌(发的也是编号,为了保证编号是排序的,创建TreeSet集合接收)
            TreeSet<Integer> lqxSet=new TreeSet<Integer>();
            TreeSet<Integer> lySet = new TreeSet<Integer>();
            TreeSet<Integer> fqySet = new TreeSet<Integer>();
            TreeSet<Integer> dpSet = new TreeSet<Integer>();//底牌
    
            for(int i=0;i<array.size();i++){
                int x=array.get(i);
                if(i>=array.size()-3){
                    dpSet.add(x);
                }else if (i%3==0){
                    lqxSet.add(x);
                }else if(i%3==1){
                    lySet.add(x);
                }else if(i%3==2){
                    fqySet.add(x);
                }
            }
            //调用看牌方法
            lookPoker("林青霞", lqxSet, hm);
            lookPoker("柳岩", lySet, hm);
            lookPoker("风清扬", fqySet, hm);
            lookPoker("底牌", dpSet, hm);
    
    
    
    
        }
    
        //定义方法看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌)
        public static void lookPoker(String name,TreeSet<Integer> ts,HashMap<Integer,String> hm){
    
            System.out.println(name+"的牌是:");
    
            for (Integer key:ts){
                String poker=hm.get(key);
                System.out.print(poker+" ");
            }
            System.out.println();
    
        }
    }
    
    
  • 相关阅读:
    无题
    使用git clone 出现 Permission denied 解决办法
    为什么要用BigDecimal
    FastJson对于JSON格式字符串、JSON对象及JavaBean之间的相互转换
    版本管理规范
    Intellij Idea 常用快捷键
    AI学习资源
    Python图谱
    网站架构演化历程
    微服务系列
  • 原文地址:https://www.cnblogs.com/Javastudy-note/p/13801439.html
Copyright © 2011-2022 走看看