双列集合(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)
- 牌的表示
用数字-数字来表示(第一个数字表示花色 第二个数字表示对应的牌) - 属性
- 方法