双列集合(Map)

双列集合的特点

双列集合的特点(和单列集合对比)

单列集合每次添加一个元素,而双列集合每次添加一对元素



双列集合的框架结构

Map的常见API

英文key(键),value(值),在集合中键和值一一对应,但是键不能重复,但是值可以 重复
Map中常见的方法演示

package com.an.a;
import java.util.HashMap;
import java.util.Map;
public class MapTest {
    public static void main(String[] args) {
        /*
        V put(K key,V value) 以键值对的形式添加元素
        V remove(Object key)根据键删除键值对元素
        void clear()删除所有键值对元素
        boolean containKey(Object key)判断集合是否包含指定的键
        boolean containValue(Object value)判断集合是否包含指定的值
        boolean isEmpty()判断集合是否为空
        int size()集合的长度,也就是集合中键值对的个数
         */
        //创建Map集合对象并添加元素(因为Map是接口只能通过多态令其实例化)
        Map<String,String>  map=new HashMap<>();//实例化
        //1.put添加元素
        map.put("飞鹏","夕瑶");
        map.put("徐长卿","紫萱");
        map.put("杨过","小龙女");
        System.out.println(map);//{飞鹏=夕瑶, 杨过=小龙女, 徐长卿=紫萱}
        //put方法的细节:添加/覆盖
        //1.在添加元素的时候如果键不存在那么之间将对象添加的集合中,方法返回null
        //2.在添加元素的时候如果键存在,那么会将键值对对象覆盖,会把覆盖的值进行返回
        //细节1:
         String s = map.put("狄云", "水笙");//键没有重复将返回null
        System.out.println(s);//null
        //细节2:
         String value = map.put("杨过", "郭襄");//键重复,将会覆盖原来的键值对,并把覆盖的值进行返回
        System.out.println(value);//小龙女
        //remove删除:根据键删除键值对元素并返回被删除的键值对的值
        System.out.println(map);//集合内容:{飞鹏=夕瑶, 杨过=郭襄, 狄云=水笙, 徐长卿=紫萱}
        String value1 = map.remove("飞鹏");
        System.out.println(value1);//夕瑶
        System.out.println(map);//{杨过=郭襄, 狄云=水笙, 徐长卿=紫萱}
        //判断集合是否包含指定的键containsKey
         boolean result1 = map.containsKey("杨过");
        System.out.println(result1);//true
        //判断集合是否包含指定的值containsValue
         boolean result2 = map.containsValue("张飞");
        System.out.println(result2);//false
        //判断集合是否为空isEmpty
         boolean result3= map.isEmpty();
        System.out.println(result3);//false
        //得出集合长度:键值对的对数
         int result4 = map.size();
        System.out.println(result4);//3
        //清空集合clear
        map.clear();
        System.out.println(map);//{}

    }
}

Map的遍历方式


第一种遍历方式:通过键找值


将所有的键都放到一个单列集合中然后遍历这个集合,通过get方法依次找到每一个键的值
第一种遍历方式演示

package com.an.a;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MapTest {
    public static void main(String[] args) {
    //Map的第一种遍历方法(键找值)
        Map<String,String>map=new HashMap<>();
        map.put("杨过","小龙女");
        map.put("飞鹏","夕瑶");
        map.put("喜羊羊","美羊羊");
        //1.获取所有的键并把这些键放在一个单列集合中
        Set<String> keys = map.keySet();
        //2.可以通过单列集合的遍历方式遍历键(也可以时候迭代器或者lamda表达式)
        for (String key : keys) {//遍历键
             String value = map.get(key);//get方法返回此键对应的值如果此键不包含值则返回null
            System.out.println(key+"="+value);
        }

    }
}
//output:
/*杨过=小龙女
  飞鹏=夕瑶
   喜羊羊=美羊羊
*/

第一种遍历方式:通过键找值,先通过keySet方法,将所有的键放在一个单列集合中,然后遍历这个单列集合依次得到键,然后通过Map的get方法,通过键寻找值,然后得到键值对

Map的第二种遍历方式(键值对)

  • 第二种遍历方式演示
package com.an.a;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MapTest {
    public static void main(String[] args) {
    //Map的第2种遍历方法(键值对)
        Map<String,String>map=new HashMap<>();
        map.put("杨过","小龙女");
        map.put("飞鹏","夕瑶");
        map.put("喜羊羊","美羊羊");
    //通过键值对对象进行遍历
        //1.通过一个方法返回所有的键值对对象,返回一个set集合
        /*
        该方法将所有的键值对对象都装进一个set集合并返回set集合
        返回表示该集合内元素的类型,外层泛型表示该元素为一个Entry(键值对对象),内层泛型表示该元素里面的内容
         */
         Set<Map.Entry<String, String>> entries = map.entrySet();
         //2.遍历entries这个set集合得到每一个键值对对象
        for (Map.Entry<String, String> entry : entries) {
            //Entry为Map接口的一个子接口,也可以不写Map.直接写Entry,但是需要在开头导入Entry接口
            //3.用getKey和getValue方法依次得到键和值
            String key=entry.getKey();//得到键
            String value=entry.getValue();//得到值
            System.out.println(key+"="+value);

        }

    }
}
/*output:
        杨过=小龙女
        飞鹏=夕瑶
        喜羊羊=美羊羊*/

Map集合的Lambda遍历

package com.an.a;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

public class LambdaPractice {
    public static void main(String[] args) {
    /*
        Map的Lambda遍历
        所用到的方法:
        default void forEach(BiConsumer<? super K,? super V>action)
     */
        //创建对象并添加元素
        Map<String,String> map = new HashMap<>();
        map.put("张三","老子天下第一");
        map.put("李四","真相只有一个");
        map.put("王五","元芳你怎么看");
        /*
        通过查看forEach源码:
        forEach底层通过获取键值对对象的方式遍历Map集合,并依次将得到的键和值交给accept进行处理
        BiConsumer是一个接口并且是一个函数式接口可以用Lambda进行操作
         */
        //使用匿名内部类操作
        /*map.forEach(new BiConsumer<String, String>() {
            @Override
            public void accept(String key, String value) {
                System.out.println(key+"="+ value);
            }
        });*/

        //使用Lambda进行简化
        map.forEach(( key, value)-> System.out.println(key+"=" +value)    );
           
                

    
    }
}

HashMap的基本使用


在HashMap中不需要学习新的方法,直接只要Map里面的方法即可

在学习HashMap时候,建议对比和HashSet的异同点

HashMap练习1(储存自定义对象)

  • 题目1

HashMap和HashSet的实现形似,他们实现对不同对象的辨别依赖的是equals和havaCode方法,当对象的类型是java官方自己写的类,其已经实现好了对equals和havaCode方法的覆写,可以实现对不同对象的辨别,但是当我们对象的类型是我们自己定义的类时,此时需要我们自己手动覆写equals和havaCode方法,以达到辨别不同对象,进而实现该类的特点

双列集合所表现出来的特点都是由键所决定的,和值没有任何关系

package com.an.a;
import java.util.*;

/*
    需求:
    创建一个HashMap集合,键是学生对象,值是籍贯
    储存3个键值对元素,并遍历
    要求:同姓名 同年龄认为是同一个学生
 */
class StudentS{
    private String name;
    private int age;

    public StudentS() {
    }

    public StudentS(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "StudentS{name = " + name + ", age = " + age + "}";
    }
    //覆写equals和havaCode方法

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        StudentS studentS = (StudentS) o;
        return age == studentS.age && Objects.equals(name, studentS.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

public class MapTest {
    public static void main(String[] args) {
        //创建学生对象
       StudentS s1=new StudentS("张三",23);
       StudentS s2=new StudentS("李四",24);
       StudentS s3=new StudentS("王五",25);
       StudentS s4=new StudentS("王五",25);

        //1.将学生对象添加到HashMap中
        HashMap<StudentS,String>hm=new HashMap<>();
        hm.put(s1,"湖北");
        hm.put(s2,"湖南");
        hm.put(s3,"江西");
        hm.put(s4,"新疆");//此时判定键和集合中的已有的键完全相同,进行了替换,变成了{王五,25}=新疆
        System.out.println(hm);
      //2.遍历集合
        //2.1通过键寻找值遍历
        Set<StudentS> studentS = hm.keySet();//将所有的键储存在Set中
        for (StudentS student : studentS) {//遍历Set中的所有键并通过键寻找值
          String value = hm.get(student);//得到键的值
            System.out.println(student+"="+value);
        }
   //2.2通过键值对遍历
        Set<Map.Entry<StudentS, String>> entries = hm.entrySet();//将所有的键值对都放到Set集合中
        for (Map.Entry<StudentS, String> entry : entries) {
          StudentS key = entry.getKey();//得到键
            String value = entry.getValue();//得到值
            System.out.println(key+"="+value);
        }

    }
}


HashMap练习2(利用HashMap集合进行统计)

该题按照原始的做法,有4个景点可以定义4个变量作为计数器,当该景点被投票一次则该计数器+1,但是如果我们的景点数量过多或者我们的景点数量不确定,则该做法就太繁琐了

我们可以按照Map集合的特点来做,将Map集合的键定义为景点,将值定义为投票的次数,先判断在集合中是否存在该景点,如果不存在则将该景点和1添加到集合中,如果已经存在了该景点则将景点存在的次数+1

package com.an.a;
import java.util.*;

/*
    某个班级80名学生,现在需要组成秋游活动
    班长提供了A B C D 4个景点
    每个学生只能选择1个景点,请统计出哪个景点想去的人数最多
 */


public class MapTest {
    public static void main(String[] args) {
        //模拟80名学生随机投票
        //定义数组储存4个景点(用集合储存也可以)
        String[]arr={"A","B","C","D"};
      //用随机数模拟80个同学投票并把投票的结果储存起来
      Random r=new Random();
      ArrayList<String> list=new ArrayList<>();//储存所用的投票结果
        for (int i = 0; i < 80; i++) {
            int index = r.nextInt(arr.length);//随机获取景点数组的索引
            list.add(arr[index]);//将索引对应的投票添加到集合中
        }
        //目前已经取得了所用的投票结果
        HashMap<String,Integer>hm=new HashMap<>();//用于储存景点和其所对应的投票数
        //如果景点数太多,不方便用计数器进行统计,可以用Map集合进行统计
        //遍历所有的投票结果
        for (String name : list) {
            //判断给景点在Map集合中是否存在
            if(hm.containsKey(name)){
                //存在
                //获取此时的投票次数
                Integer count = hm.get(name);//获取该将景点对应的次数
                count++;//将该次数+1
                //将该景点新的次数添加到集合中
                hm.put(name,count);
            }else{//该景点不存在
                hm.put(name,1);//将新的景点和第一次添加到集合中
            }
        }
        System.out.println(hm);//{A=23, B=18, C=20, D=19}
        //投票已经完成
        //统计哪个景点想去的人数最多
        //遍历Map集合找出最多的次数
       Set<Map.Entry<String, Integer>> entries = hm.entrySet();//将所有的键值对对象放在Set集合中
        int max=0;
        for (Map.Entry<String, Integer> entry : entries) {
            int value=entry.getValue();//依次得到每一个景点对应的投票次数
            if(value>max)
                max=value;
        }
       //此时的max记录的是最大的投票数
        //通过最大的投票数找出相对应的景点
        for (Map.Entry<String, Integer> entry : entries) {
           if(entry.getValue()==max)
               System.out.println(entry.getKey());//D

        }
    }
}


LinkedHashMap


LinkedHashMap和LinkedHashSet的实现相似,在学习的时候建议对比来学习

package com.an.a;

import java.util.LinkedHashMap;

/*
    LinkedHashMap
    由键决定:
    有序 不重复 无索引
    有序:保证储存和取出的顺序一致
    原理:
    底层数据结构依然是哈希表 只是每个键值对元素又多了一个双链表机制记录储存的顺序
 */
public class LinkedHashMapTest {
    public static void main(String[] args) {
        //创建集合
        LinkedHashMap<String,Integer> lhm=new LinkedHashMap<>();
        //添加元素
        lhm.put("a",123);
        lhm.put("a",123);
        lhm.put("b",456);
        lhm.put("c",789);
        System.out.println(lhm);//{a=123, b=456, c=789}(储存的顺序和取出的顺序一致,并且保证不重复)
    }
}

TreeMap

  • TreeMap的特点

    TreeMap的基本应用

    当键的类是java自己实现的类时,排序是按照升序进行的(数字相关的类是按照数字的升序排序的,而String类是按照ASCII表的顺序进行排列的),这和java自己写的类所实现的Comprable接口有关
package com.an.a;

import java.util.TreeMap;

/*
    需求:
    键:整数表示id
    值:字符串表示商品的名称
    要求1:按照id的升序排列
    要求2:按照id的降序排列
 */
public class TreeMapTest {
    public static void main(String[] args) {
        TreeMap<Integer,String> tm = new TreeMap<>();
        //添加元素
        tm.put(1,"铅笔");
        tm.put(4,"口罩");
        tm.put(5,"橡皮");
        tm.put(2,"手机");
        System.out.println(tm);//{1=铅笔, 2=手机, 4=口罩, 5=橡皮}(按照默认的排序规则键是按照升序排列的)
    }
}

package com.an.a;

import java.util.Comparator;
import java.util.TreeMap;

/*
    需求:
    键:整数表示id
    值:字符串表示商品的名称
    要求1:按照id的升序排列
    要求2:按照id的降序排列
 */
public class TreeMapTest {
    public static void main(String[] args) {
        TreeMap<Integer,String> tm = new TreeMap<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;//当不知道怎样是降序,可以自己进行尝试
            }
        });
        //添加元素
        tm.put(1,"铅笔");
        tm.put(4,"口罩");
        tm.put(5,"橡皮");
        tm.put(2,"手机");
     //要实现键降序排列默认的排序规则没有用,需要自己实现比较器的排序
        System.out.println(tm);//{5=橡皮, 4=口罩, 2=手机, 1=铅笔}
    }
}

TreeMap练习2-键位置添加自定义对象

package com.an.a;

import java.util.Comparator;
import java.util.TreeMap;

/*
    键:学生对象
    值:籍贯
    要求:按照学生年龄的升序排列,年龄一样按照姓名的字母排列,同姓名同年龄视为同一个人
 */
class Studentss implements  Comparable<Studentss>{
    private String name;
    private int age;

    public Studentss() {
    }

    public Studentss(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Studentss{name = " + name + ", age = " + age + "}";
    }

    @Override//覆写接口的抽象方法 指定比较规则
    public int compareTo(Studentss o) {
        //按照学生年龄的升序排列,年龄一样按照姓名字母排列,同姓名年龄视为同一个人
        //this:表示当前要添加的元素
        //o:表示已经在红黑树中存在的元素
        //返回值
        /*
        负数:表示当前要添加的元素是小的,存左边
        正数:表示当前要添加的元素是大的,存右边
        0:表示当前要添加的元素在红黑树中已经存在,舍弃

         */
        int i=this.getAge()-o.getAge();
        i=i==0?this.getName().compareTo(o.getName()):i;
        return i;
    }
}
public class TreeMapTest {
    public static void main(String[] args) {
        //创建集合
        TreeMap<Studentss,String>tm = new TreeMap<>();
        //创建3个学生对象
        Studentss s1 = new Studentss("张三",23);
        Studentss s2 = new Studentss("李四",24);
        Studentss s3 = new Studentss("王五",25);
        //添加元素
        tm.put(s1,"湖北");
        tm.put(s2,"湖南");
        tm.put(s3,"新疆");
      //TreeMap需要排序,当自定义的类没有指定排序规则时,将会报错(ClassCastException)
        //已经指定比较规则
        System.out.println(tm);
    }
}


TreeMap练习--利用TreeMap进行统计

package com.an.a;

import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;



public class TreeMapTest {
    public static void main(String[] args) {

     /*
        需求:
        字符串“abbbbaaaacccdde”
        请统计出每一个字符出现的次数并按照以下格式输出
        a(5)b(4)c(3)d(4)
        统计--->计数器思想
        弊端:如果我们统计的字符很多,将会非常不方便
        用map集合进行统计
      */
        //和利用HashMap统计非常相似
        //1.定义字符串
        String str="abbbbaaaacccdde";
        //2.遍历字符串并将它和出现的次数添加到TreeMap集合中
        TreeMap<Character,Integer> tm = new TreeMap<>();//储存字符和其所出现的次数
        for(int i=0;i<str.length();i++){
            char c = str.charAt(i);//获取每一个字符
            if(tm.containsKey(c)){
                //如果该字符已经在集合中
               Integer count = tm.get(c);//获取该字符出现的次数
                count++;//将该次数+1
                tm.put(c,count);//将新的键值对添加进集合并替换以前的键值对
            }else {
                //如果在集合中没有该字符
                tm.put(c,1);

            }
        }
        System.out.println(tm);//{a=5, b=4, c=3, d=2, e=1}
        //此时的格式不符合要求
        //遍历集合并按照格式要求输出

        //按照键值对输出
         Set<Map.Entry<Character, Integer>> entries = tm.entrySet();//将所有的键值对放进Set集合中
        for (Map.Entry<Character, Integer> entry : entries) {
            System.out.printf(entry.getKey()+"(%d)",entry.getValue());//a(5)b(4)c(3)d(2)e(1)
        }
       //按照键找值输出
         Set<Character> keys = tm.keySet();//将所有的键都放在一个Set集合中
        for (Character key : keys) {
          Integer value = tm.get(key);//通过键找到所对应的值
            System.out.printf(key+"(%d)",value);//a(5)b(4)c(3)d(2)e(1)
        }

        //StringBulder拼接(键值对)(用StringBuffer 和StringJoiner也可以实现)
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<Character, Integer> entry : entries) {//该部用迭代器或者Lamda都可以实现
            sb.append(entry.getKey()).append("(").append(entry.getValue()).append(")");
        }
        System.out.println(sb);//a(5)b(4)c(3)d(2)e(1)
        
    }
}

可变参数

package com.an.a;
//验证可变参数的使用
public class Chagedargs {
    public static void main(String[] args) {
        /*
            计算1个数据的和
            计算2个数据的和
            计算3个数据的和
            计算n个数据的和
         */
        //传统做法计算多个数据的和可以传递数组
        int arr[]={1,2,3,4,5,6,7,8,9,10};
        System.out.println(getSum(arr));//55
        //但是当我们的数据的个数是不确定的,那该怎么办
    }
    public static int getSum(int arr[]){
        int sum=0;//记录总和
        for (int i : arr) {
            sum=sum+i;
        }
        return sum;
    }
}

  • 可变参数实现
package com.an.a;
//验证可变参数的使用
public class Chagedargs {
    public static void main(String[] args) {
        /*
            计算1个数据的和
            计算2个数据的和
            计算3个数据的和
            计算n个数据的和
         */
    //可变参数
        System.out.println(getSum(1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10));//58
        System.out.println(getSum(1, 2));//3
        //可变参数底层分析:
        /*
            可变参数底层就是一个数组
            只不过不需要我们自己创建,java已经帮我们创建好了
            可变参数的细节:
            1.在方法的形参中最多只能写一个可变参数(通过可变参数的特点很容易理解,将可变参数理解为一个大胖子,有多少吃多少)
            2.如果在方法中除了可变参数还有其他形参,那么可变参数要写在最后(根据可变参数的特点也很好理解)
         */

    }
    public static int getSum(int ...arr){//可以传递任意个Int类型的数据
        int sum=0;
        for (int i : arr) {
            sum+=i;
        }
        return sum;
    }
}

Collections


package com.an.a;

import java.util.ArrayList;
import java.util.Collections;

//Collections符合工具类特点,构造方法被私有化,方法都是静态的
public class CollectionsTest {
    public static void main(String[] args) {
        /*
            public static<T> boolean addAll(Collection<T>c,T...elements)批量添加元素(只能添加Collection类型的元素)
            pubilc static void shuffle(List<?>list)打乱List集合元素的顺序
         */
        ArrayList<String> list = new ArrayList<>();
        //批量添加元素
        Collections.addAll(list,"abc","123","diei","456","hhhd");
        System.out.println(list);//[abc, 123, diei, 456, hhhd]
        //打乱List集合元素的顺序
        Collections.shuffle(list);
        
        System.out.println(list);//[hhhd, 123, diei, 456, abc]
        //顺序成功被打乱
    }
}

综合练习1--随机点名器的两种实现方式、

package com.an.a;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;

//关于集合的综合小练习
public class Pratice1 {
    public static void main(String[] args) {
        /*
            班级里有n个学生,实现随机点名器
         */
        //1.定义集合
        ArrayList<String> list = new ArrayList<>();
        //2.添加数据
        Collections.addAll(list,"郝建","范统","范县","张飞","马超","赵云","邢道荣");
        //3.随机点名
        //3.1用Random
        Random r = new Random();
       int i = r.nextInt(list.size());//获取[0,6)的随机数(即索引)
       String name = list.get(i);//通过索引获取对应的姓名
        System.out.println(name);
        //3.2通过打乱集合来实现
        Collections.shuffle(list);//将list进行打乱
       String name1 = list.get(0);//每次都获取0索引上的元素,但是打乱后每次的元素都不一样
        System.out.println(name1);
    }
}

package com.an.a;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;

//关于集合的综合小练习
public class Pratice1 {
    public static void main(String[] args) {
     /*
     自动点名器:
     班级里有n个学生
     要求:
     70%的几率随机到男生
     30%的几率随机到女生
      */
        /*
        这题的主要问题是解决概率问题
        对于可以模拟出概率的情况,根据结果看看是随机男生还是随机女生
         */
        //1.模拟概率
        ArrayList<Integer>list = new ArrayList<>();
        //集合中7个1,3个0,随机得到1的几率为70%,随机得到0的几率为30%
        Collections.addAll(list,1,1,1,1,1,1,1,0,0,0);
        //通过打乱集合元素实现随机抽取
        Collections.shuffle(list);
        //从集合中随机抽取0获取1
        Random r = new Random();
        int index = r.nextInt(list.size());//获取list的随机索引
         Integer number = list.get(index);//通过索引得到随机元素


        //我们通过这个解决了概率问题,如果随机为1则点名男生,如果随机为0则点名女生

        //2.定义2个集合分别储存男孩名和女孩名
        ArrayList<String> boylist = new ArrayList<>();
        Collections.addAll(boylist,"郝建","范统","唐三","萧炎","霍宇浩");

      ArrayList<String> girllist = new ArrayList<>();
      Collections.addAll(girllist,"波塞西","小舞","白雪公主","王语嫣");

     //3.判断得到的是0还是1 以决定是从男孩名中抽取还是从女孩名中抽取
        if(number==1){
            //boylist
           int boyindex= r.nextInt(boylist.size());//获取男孩集合中随机索引
           String name = boylist.get(boyindex);//获取随机的男孩名
            System.out.println(name);
        }else {
            //girllist
            int girlindex= r.nextInt(girllist.size());//获取男孩集合中随机索引
            String name = girllist.get(girlindex);//获取随机的男孩名
            System.out.println(name);
        }


    }
}

idea快捷键 shif+f6批量改名

综合练习3---不重复的随机点名

package com.an.a;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;

//关于集合的综合小练习
public class Pratice1 {
    public static void main(String[] args) {
    /*
    班级里有n个学生
    要求:
    被点到的学生不会再被点到
    但是如果班级中所有的学生都点完了,需要重新开启第二轮点名
     */
        //思路:
        //被点到的学生不会再被点到,可以点到一个学生,然后将集合中的这个学生删除
        //学生被点完了,重新开启第二轮点名,可以在内层循环循环点完所有学生,然后开始第二层外层循环
        
        //假设有10个学生
        //1.创建集合储存学生
        ArrayList<String> list1 = new ArrayList<>();
        //2.添加学生
        Collections.addAll(list1,"王亦可","夏阳","洋洋","徐橙","刘飞","赵子龙","徐曼","汪曼春","天天","小王");
        int size=list1.size();
        Random r = new Random();
        ArrayList<String> list2 = new ArrayList<>();//用于临时储存被删除的姓名(list2作为过渡集合)
        for (int i = 0; i < 10; i++) {
            System.out.printf("----------------------现在开始第%d轮点名:-----------------\n",i+1);
            for (int j = 0; j < size; j++) {//如果此时是i<list.size() 将会因为集合内元素的删除 集合长度减小,而无法点名所有的学生
                int index = r.nextInt(list1.size());//得到集合的随机下标
                //String name = list.get(index);//得到随机的姓名
                //list.remove(name);//将姓名删除
                //将注释的2步合成1步
                String name = list1.remove(index);//刚好此时的remove方法返回被删除的姓名就是所得到的随机姓名
                System.out.println(name);
                list2.add(name);//将删除的姓名进行储存

            }
            //该循环结束后list1中为空 list2中储存了所有的姓名
            list1.addAll(list2);//将list2中所有的元素添加到list1中
            list2.clear();//将list2中的所有的元素清空
        }
    }
}

带权重的随机点名


带权重的随机算法


综合练习6集合嵌套

package com.an.a;

import java.util.*;

//关于集合的综合小练习
public class Pratice1 {
    public static void main(String[] args) {
   /*
   需求:
   定义一个Map集合,用键表示省份名称province,用值表示市,但是市会有多个。
   添加完毕后,遍历结果格式如下:
   江苏省 = 南京市,扬州市,常州石,苏州市,宿迁市
   湖北省 = 荆州市,孝感市,黄冈市,随州市,十堰市
   河北省 = 石家庄市,唐山市,邢台市,张家口市,保定市
    */
        /*
        该题的键很明显表示省份,而值有多个,可以用一个集合表示值,集合中储存所有的市
         */
        ArrayList<String> city1 = new ArrayList<>();
        ArrayList<String> city2 = new ArrayList<>();
        ArrayList<String> city3 = new ArrayList<>();
        Collections.addAll(city1,"南京市","扬州","常州市","苏州市","宿迁市");
        Collections.addAll(city2,"荆州市","孝感市","黄冈市","随州市","十堰市");
        Collections.addAll(city3,"石家庄市","唐山市","邢台市","张家口市","保定市");
        //1.将一个省份和所对应的多个市添加到Map集合中
        HashMap<String,ArrayList<String>> hm = new HashMap<>();
        hm.put("江苏省",city1);
        hm.put("湖北省",city2);
        hm.put("河北省",city3);
        System.out.println(hm);//{江苏省=[南京市, 扬州, 常州市, 苏州市, 宿迁市], 湖北省=[荆州市, 孝感市, 黄冈市, 随州市, 十堰市], 河北省=[石家庄市, 唐山市, 邢台市, 张家口市, 保定市]}
        //默认的打印不符合要求:需要遍历
         Set<Map.Entry<String, ArrayList<String>>> entries = hm.entrySet();//将所有的键值对对象放进set集合中
        for (Map.Entry<String, ArrayList<String>> entry : entries) {
           String key = entry.getKey();//得到省份
           ArrayList<String> value = entry.getValue();//得到值的集合
            //由于值也是一个集合,此时也需要遍历集合并把市拼接成对应的格式
            StringJoiner sb = new StringJoiner(",");//相隔元素为,

            for (String s : value) {//遍历值的集合
                sb.add(s);
            }
           //将键和值拼接成指定格式
            System.out.println(key+ "=" +sb);

        }
    }
}

阶段项目--斗地主小游戏


控制台版意思是没有图形界面

一般我们将程序的入口放在main方法中,而具体的实现逻辑,放在单独的类中
前3张牌作为底牌

完整代码

  • 1.main方法
package com.an.a;

public class App {
    public static void main(String[] args) {
        //main方法是我们程序的入口,当我们创建一个PokerGame对象表示我们的游戏开始启动
        new PokerGame();//游戏开始
    }
}

  • 2.PokerGame
package com.an.a;
/*
  "❤" ,"🖤" ,"♣","♦"
  "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2",
 */

import java.util.ArrayList;
import java.util.Collections;

public class PokerGame {
    static  ArrayList<String> list = new ArrayList<>();//list为牌盒用来装牌
    static {
        //准备牌:静态块会随着类的加载而执行,而且只会被执行一次(可以理解为不管这个游戏进行多少次,用的都是一副牌)
        //1.用字符串数组(集合也可以)储存牌的2部分
        String[]color = {"❤" ,"🖤" ,"♣","♦"};
        String[]number = {  "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2",};
        //2.用集合储存依次拼接的牌

        for (String c : color) {
            for (String n : number) {
                list.add(c+n);//将拼接后的牌放进牌盒中
            }
        }
        list.add("大王");
        list.add("小王");

    }
    public PokerGame(){//当创建对象时将自动调用空参构造

        //  洗牌
    Collections.shuffle(list);


    //发牌:将牌分发给玩家,需要使用4个集合储存(只有3个玩家),但是要储存底牌
        ArrayList<String> dipai = new ArrayList<>();
        ArrayList<String> play1 = new ArrayList<>();
        ArrayList<String> play2 = new ArrayList<>();
        ArrayList<String> play3 = new ArrayList<>();
        //1.开始发牌:前3张牌作为底牌,并且,后面的牌依次每个玩家分发一张
        //1.1遍历牌盒:并将相应的牌储存到玩家和底牌集合中
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            if(i<=2){//前3张牌为底牌

                dipai.add(s);//将前3张牌添加到底牌集合中
                continue;
            }
            //后面依次给每一个玩家分发牌:可以根据i%3的结果决定给哪一个玩家发牌
            if(i%3==0) {//给play1发牌
                play1.add(s);
            }else if(i%3==1){//给play2发牌
                play2.add(s);
            }else {
                play3.add(s);
            }

        }
//看牌
      lookPoker("底牌",dipai);
        lookPoker("张三",play1);
        lookPoker("李四",play2);
        lookPoker("王五",play3);
    }
/*
参数1:玩家名
参数2:每位玩家的牌
 */
    public  void lookPoker(String name,ArrayList<String> list){
        System.out.print(name+": ");
        for (String poker : list) {
            System.out.print(poker+" ");
        }
        System.out.println();
    }


}

项目02给牌排序

第一种排序方式

  • 利用序号进行排序

    我们先把牌按照指定的规则先手动进行排列,然后再和1,2,3...这样的数字一 一对应,数字越大对应的牌就越大


谁为主导,谁就为键
快捷键 ctrl+shif+/ 多行注释

对于排序,如果我们直接进行排序将会比较复杂,当我们将我们的牌储存到可以排序的集合中,发现不管了默认排序还是比较器排序,都不能满足我们的排序规则。对此我们可以使用序号对应的思想,将我们的牌和唯一的序号一一对应(用map实现),我们只需要对序号进行排序,然后通过序号得到具体的牌,进而实现了对牌的排序

通过序号对应改善准备牌 洗牌 发牌的操作 并排序

  • 准备牌期间的细节
package com.an.a;
/*
  "❤" ,"🖤" ,"♣","♦"
  "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2",
 */

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;

public class PokerGame {
    static {
        //用数组储存牌的2部分
        String[] color = {"❤", "🖤", "♣", "♦"};
        String[] number = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2",};
        int serizNumber = 1;//表示序号
        //用HashMap储存牌和其所对应的序号(该步不需要排序用HashMap而不用TreeMap,HashMap性能更好)
        HashMap<Integer, String> hm = new HashMap<>();//用于储存牌和其所对应的序号
        for (String s : color) {
            for (String s1 : number) {
                hm.put(serizNumber, s + s1);
                serizNumber++;

            }
        }
        System.out.println(hm);
        /*
        {1=❤3, 2=❤4, 3=❤5, 4=❤6, 5=❤7, 6=❤8, 7=❤9, 8=❤10, 9=❤J, 10=❤Q, 11=❤K, 12=❤A, 13=❤2,
         14=🖤3, 15=🖤4, 16=🖤5, 17=🖤6, 18=🖤7, 19=🖤8, 20=🖤9, 21=🖤10, 22=🖤J, 23=🖤Q, 24=🖤K, 25=🖤A, 26=🖤2, 
         27=♣3, 28=♣4, 29=♣5, 30=♣6, 31=♣7, 32=♣8, 33=♣9, 34=♣10, 35=♣J, 36=♣Q, 37=♣K, 38=♣A, 39=♣2, 
         40=♦3, 41=♦4, 42=♦5, 43=♦6, 44=♦7, 45=♦8, 46=♦9, 47=♦10, 48=♦J, 49=♦Q, 50=♦K, 51=♦A, 52=♦2}

         */
        //如果此时将序号排序,并抽取到的序号为1 2 3 4 14
        //排序后的牌为:❤3 ❤4 ❤5 ❤6 🖤3
        //可以发现 按照这样的方式准备牌 将序号排序后 但是牌还是没有按照人习惯的顺序排序
    }
}

将匹配牌的循环对调,即依次循环完每张牌的所有花色

  • 改进后
package com.an.a;
/*
  "❤" ,"🖤" ,"♣","♦"
  "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2",
 */

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;

public class PokerGame {
    static {
        //用数组储存牌的2部分
        String[] color = {"❤", "🖤", "♣", "♦"};
        String[] number = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2",};
        int serizNumber = 1;//表示序号
        //用HashMap储存牌和其所对应的序号(该步不需要排序用HashMap而不用TreeMap,HashMap性能更好)
        HashMap<Integer, String> hm = new HashMap<>();//用于储存牌和其所对应的序号
        for (String s1 :number) {
            for (String s : color) {
                hm.put(serizNumber, s + s1);
                serizNumber++;

            }
        }
        System.out.println(hm);
        /*
        {1=❤3, 2=❤4, 3=❤5, 4=❤6, 5=❤7, 6=❤8, 7=❤9, 8=❤10, 9=❤J, 10=❤Q, 11=❤K, 12=❤A, 13=❤2,
         14=🖤3, 15=🖤4, 16=🖤5, 17=🖤6, 18=🖤7, 19=🖤8, 20=🖤9, 21=🖤10, 22=🖤J, 23=🖤Q, 24=🖤K, 25=🖤A, 26=🖤2,
         27=♣3, 28=♣4, 29=♣5, 30=♣6, 31=♣7, 32=♣8, 33=♣9, 34=♣10, 35=♣J, 36=♣Q, 37=♣K, 38=♣A, 39=♣2,
         40=♦3, 41=♦4, 42=♦5, 43=♦6, 44=♦7, 45=♦8, 46=♦9, 47=♦10, 48=♦J, 49=♦Q, 50=♦K, 51=♦A, 52=♦2}
         //改进后
         {1=❤3, 2=🖤3, 3=♣3, 4=♦3, 5=❤4, 6=🖤4, 7=♣4, 8=♦4, 9=❤5, 10=🖤5, 11=♣5, 12=♦5, 13=❤6, 14=🖤6, 
         15=♣6, 16=♦6, 17=❤7, 18=🖤7, 19=♣7, 20=♦7, 21=❤8, 22=🖤8, 23=♣8, 24=♦8, 25=❤9, 26=🖤9, 27=♣9, 28=♦9, 29=❤10, 
         30=🖤10, 31=♣10, 32=♦10, 33=❤J, 34=🖤J, 35=♣J, 36=♦J, 37=❤Q, 38=🖤Q, 39=♣Q, 40=♦Q, 41=❤K, 42=🖤K, 43=♣K, 44=♦K, 45=❤A, 46=🖤A, 47=♣A, 48=♦A, 49=❤2, 50=🖤2, 51=♣2, 52=♦2}


         */
        //如果此时将序号排序,并抽取到的序号为1 2 3 4 14
        //排序后的牌为:❤3 ❤4 ❤5 ❤6 🖤3
        //改进后的牌为:❤3 🖤3 ♣3 ♦3 🖤6
        //可以发现 按照这样的方式准备牌 将序号排序后 但是牌还是没有按照人习惯的顺序排序
        
    }
}

很完美的解决了这个问题

package com.an.a;
/*
  "❤" ,"🖤" ,"♣","♦"
  "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2",
 */

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;

public class PokerGame {
    static  HashMap<Integer, String> hm = new HashMap<>();//用于储存牌和其所对应的序号
    static ArrayList<Integer> list = new ArrayList<>();//因为我们要操作序号,所有为了方便将其单独存在集合中
    static {
        //准备牌
        //用数组储存牌的2部分
        String[] color = {"❤", "🖤", "♣", "♦"};
        String[] number = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2",};
        int serizNumber = 1;//表示序号
        //用HashMap储存牌和其所对应的序号(该步不需要排序用HashMap而不用TreeMap,HashMap性能更好)

        for (String s1 :number) {
            for (String s : color) {
                hm.put(serizNumber, s + s1);
               list.add(serizNumber);//将所有的序号单独存在一个集合中
                serizNumber++;

            }
        }

        hm.put(serizNumber,"小王");
        list.add(serizNumber);
        serizNumber++;
        hm.put(serizNumber,"大王");
        list.add(serizNumber);


    }
    public PokerGame(){
        //洗牌
        Collections.shuffle(list);//将序号打乱就相当于洗牌
        //发牌(这里用TreeSet储存分发的牌,可以按照默认自动升序排序,进而相当于已经给牌进行排序了)
        //这里也可以用ArrayList,只是多了排序的一步
        TreeSet<Integer> dipai = new TreeSet<>();
        TreeSet<Integer> play1 = new TreeSet<>();
        TreeSet<Integer> play2 = new TreeSet<>();
        TreeSet<Integer> play3 = new TreeSet<>();
        //具体发牌
        for (int i = 0; i < list.size(); i++) {
            Integer serizNumber = list.get(i);//根据下标取出序号
            String poker = hm.get(serizNumber);//通过序号取出具体的牌
            if(i<=2){//前3张牌作为底牌

               dipai.add(serizNumber);//将底牌储存起来
                continue;
            }
            if(i%3 == 0){//值为0则分发给玩家1
              play1.add(serizNumber);

            }else if(i%3==1){
                play2.add(serizNumber);
            }else{
                play3.add(serizNumber);
            }
        }
        //看牌
        lookPoker("底牌",dipai);
        lookPoker("张三",play1);
        lookPoker("李四",play2);
        lookPoker("王五",play3);



    }

    //参数1 玩家姓名 参数2 每位玩家的牌
    public void lookPoker(String name,TreeSet<Integer> ts) {
        System.out.print(name + ":  ");
        for (Integer t : ts) {
             String value = hm.get(t);//根据键找到具体的牌
            System.out.print(value+" ");
        }
        System.out.println();
    }
}

运行结果:

第二种排序方式



具体的价值可以自己任意规定,但是要满足斗地主的牌的大小规则

一个异常

  • getValue方法
  public int getValue(String poker){
        //获取牌上的数字
        String number = poker.substring(1);//获取牌上是数字
        //拿着数字在map集合中判断是否存在
        //存在 获取价值
        //不存在 ,作类型转化
        if(hm.containsKey(number)){//如果在map中存在
            //存在 获取价值
            return hm.get(number);//通过数字获取其价值
        }else {
          //不存在该数字 做类型转化
          return Integer.parseInt(number);//此时的牌的数字就是其价值 直接作类型转化即可
        }


按照该代码将会出现数字格式化异常

  • 原因
    其中有一步是截取ArrayList中储存的牌的数字部分,看看在map集合中是否存在,但是其中没有考虑到大王和小王的特殊性,即大王和小王没有花色部分,即截取的是王这个字符,然后被误判map中没有,然后进行格式化转化,最后导致数字格式化异常

修改,在储存大王小王和对应的价值时,在大王和小王前面空一格,这样在截取的时候,截取的就是大王 小王,从而不会误判

//此代码有误,待以后重新测试
package com.an.a;
/*
  "❤" ,"🖤" ,"♣","♦"
  "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2",
 */

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;

public class PokerGame {
    static  ArrayList<String> list = new ArrayList<>();//list为牌盒用来装牌
    static HashMap<String,Integer> hm = new HashMap<>();//键表示牌上的数字 值表示牌的价值
    static {
        //准备牌:静态块会随着类的加载而执行,而且只会被执行一次(可以理解为不管这个游戏进行多少次,用的都是一副牌)
        //1.用字符串数组(集合也可以)储存牌的2部分
        String[]color = {"❤" ,"🖤" ,"♣","♦"};
        String[]number = {  "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2",};
        //2.用集合储存依次拼接的牌

        for (String c : color) {
            for (String n : number) {
                list.add(c+n);//将拼接后的牌放进牌盒中
            }
        }
        list.add("大王");
        list.add("小王");
        //指定牌的价值
        //关于为什么没有设置数字的价值
        //:可以判断map集合中的键是否存在
        //如果存在,获取价值
        //如果不存在 本身的数字就是价值
        hm.put("J",11);
        hm.put("Q",12);
        hm.put("K",13);
        hm.put("A",14);
        hm.put("2",15);
       hm.put(" 大王",50);
       hm.put(" 小王",100);

    }
    public PokerGame(){//当创建对象时将自动调用空参构造

        //  洗牌
        Collections.shuffle(list);


        //发牌:将牌分发给玩家,需要使用4个集合储存(只有3个玩家),但是要储存底牌
        ArrayList<String> dipai = new ArrayList<>();
        ArrayList<String> play1 = new ArrayList<>();
        ArrayList<String> play2 = new ArrayList<>();
        ArrayList<String> play3 = new ArrayList<>();
        //1.开始发牌:前3张牌作为底牌,并且,后面的牌依次每个玩家分发一张
        //1.1遍历牌盒:并将相应的牌储存到玩家和底牌集合中
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            if(i<=2){//前3张牌为底牌

                dipai.add(s);//将前3张牌添加到底牌集合中
                continue;
            }
            //后面依次给每一个玩家分发牌:可以根据i%3的结果决定给哪一个玩家发牌
            if(i%3==0) {//给play1发牌
                play1.add(s);
            }else if(i%3==1){//给play2发牌
                play2.add(s);
            }else {
                play3.add(s);
            }

        }
        //排序
        order(list);

//看牌
        lookPoker("底牌",dipai);
        lookPoker("张三",play1);
        lookPoker("李四",play2);
        lookPoker("王五",play3);
    }
    /*
    参数1:玩家名
    参数2:每位玩家的牌
     */
    public  void lookPoker(String name,ArrayList<String> list){
        System.out.print(name+": ");
        for (String poker : list) {
            System.out.print(poker+" ");
        }
        System.out.println();
    }

//排序
    public void order(ArrayList<String> list){
        Collections.sort(list, new Comparator<String>() {//利用牌的价值进行排序 需要我们自己定义比较规则
            @Override
            public int compare(String o1, String o2) {
                //o1:表示当前要插入到有序序列中的牌
                //o2:表示在有序序列中存在的牌
                //负数:o1小 插到前面
                //正数:o1大 插到后面
                //0 o1和o2的数字一样,需要按照花色再次排序
                   //计算o1的花色和价值
                 String color1 = o1.substring(0, 1);//获取花色
                int value1 = getValue(o1);//获取o1的价值
                //计算o2的花色和价值
                String color2 = o2.substring(0, 1);//获取花色
                int value2 = getValue(o2);//获取o2的价值
                //比较O1和o2的价值
                int i= value1-value2;
               return i == 0 ? color1.compareTo(color2):i ;//当数字一样 再按照花色排序

            }
        });

    }
    //计算牌的价值
    //参数:牌
    //返回值:价值
    public int getValue(String poker){
        //获取牌上的数字
        String number = poker.substring(1);//获取牌上是数字
        //拿着数字在map集合中判断是否存在
        //存在 获取价值
        //不存在 ,作类型转化
        if(hm.containsKey(number)){//如果在map中存在
            //存在 获取价值
            return hm.get(number);//通过数字获取其价值
        }else {
          //不存在该数字 做类型转化
          return Integer.parseInt(number);//此时的牌的数字就是其价值 直接作类型转化即可
        }

    }
}

登录页面(需要自己练习)


资料d25

游戏页面

如果不需要频繁的添加删除数据使用数组较好,如果需要频繁的添加删除数据则使用集合比较好

面向对象设计(poker)

  • 牌的表示
    用数字-数字来表示(第一个数字表示花色 第二个数字表示对应的牌)
  • 属性
  • 方法

游戏完善

不可变集合

posted @ 2023-01-06 09:22  一往而深,  阅读(250)  评论(0编辑  收藏  举报