day14_JAVAOOP
课程目标
1. 【理解】Set集合的特点
2. 【理解】Set集合不重复的原理
3. 【掌握】HaseSet集合的基本使用
4. 【理解】LinkedHashSet的特点
5. 【理解】Map集合的特点
6. 【掌握】HashMap的使用
7. 【理解】LinkedHashMap的特点
8. 【掌握】Map集合的案例
9. 【掌握】模拟斗地主案例
Set集合
Set集合概述
java.util.Set
接口和java.util.List
接口一样,同样继承自Collection
接口,它与Collection
接口中的方法基本一致,并没有对Collection
接口进行功能上的扩充,只是比Collection
接口更加严格了。与List
接口不同的是,Set
接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。
Set
集合有多个子类,这里我们介绍其中的java.util.HashSet
、java.util.LinkedHashSet
这两个集合。
Set集合的特点
-
Set集合中的元素不可重复
-
Set集合没有索引
总结: 无序,唯一
HashSet集合
什么是HashSet集合
java.util.HashSet
是Set
接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致)。java.util.HashSet
底层的实现其实是一个java.util.HashMap
支持,由于我们暂时还未学习,先做了解。
HashSet
是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于:hashCode
与equals
方法。
HashSet集合的特点
-
HashSet集合中的元素不可重复
-
HashSet集合没有索引
-
HashSet集合是无序的(存储元素的顺序与取出元素顺序可能不一致)
总结:无序,唯一
HashSet代码演示
/**
Collection
|--List
有序(存储顺序和取出顺序一致),可重复
|--Set
无序(存储顺序和取出顺序不一致),唯一
HashSet:它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。
注意:
虽然Set集合的元素无序,但是,作为集合来说,它肯定有它自己的存储顺序,
而你的顺序恰好和它的存储顺序一致,这代表不了有序,你可以多存储一些数据,就能看到效果。
*/
public class HashSetDemo {
public static void main(String[] args) {
//创建 Set集合
HashSet<String> set = new HashSet<String>();
//添加元素
set.add(new String("cba"));
set.add("abc");
set.add("bac");
set.add("cba");
//遍历
for (String name : set) {
System.out.println(name);
}
}
}
如何保证Hashset集合唯一?
底层依赖 两个方法:hashCode()和equals()。
步骤:
首先比较哈希值
如果相同,继续走,比较地址值或者走equals()
如果不同,就直接添加到集合中
按照方法的步骤来说:
先看hashCode()值是否相同
相同:继续走equals()方法
返回true: 说明元素重复,就不添加
返回false:说明元素不重复,就添加到集合
不同:就直接把元素添加到集合
如果类没有重写这两个方法,默认使用的Object()。一般来说一样。
而String类重写了hashCode()和equals()方法,所以,它就可以把内容相同的字符串去掉。只留下一个。
HashSet存储自定义类型元素
-
定义Student类
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } //不需要你手动重写Object hashCode和equals ,再去测试 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return age == student.age && Objects.equals(name, student.name); } @Override public int hashCode() { return Objects.hash(name, age); } }
-
定义测试类
public class HashSetDemo2 { public static void main(String[] args) { //创建集合对象 该集合中存储 Student类型对象 HashSet<Student> stuSet = new HashSet<Student>(); //存储 stuSet.add(new Student("于谦", 43)); stuSet.add(new Student("于谦", 43)); stuSet.add(new Student("郭麒麟", 23)); stuSet.add(new Student("郭麒麟", 23)); for (Student stu2 : stuSet) { System.out.println(stu2); } } }
-
结果分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RTN5ZlPY-1672475567262)(assets/image-20201119201258204.png)]
我们发现有重复的元素,不是Set集合中的元素是不能重复的吗,为什么存储了重复的元素的呢? 下面我们就来分析分析Set集合存储不重复的原理!!!
/*
* 需求:存储自定义对象,并保证元素的唯一性
* 要求:如果两个对象的成员变量值都相同,则为同一个元素。
*
* 目前是不符合我的要求的:因为我们知道HashSet底层依赖的是hashCode()和equals()方法。
* 而这hashCode()和equals()两个方法我们在学生类中没有重写,所以,默认使用的是Object类。
* 这个时候,他们的哈希值是不会一样的,根本就不会继续判断,执行了添加操作。
*/
你使用的是HashSet集合,这个集合的底层是哈希表结构。
而哈希表结构底层依赖:hashCode()和equals()方法。
如果你认为对象的成员变量值相同即为同一个对象的话,你就应该重写这两个方法。
如何重写呢? 不同担心,自动生成即可。
HashSet集合存储数据的结构
-
JDK的版本不同,HashSet集合的数据结构有所不同:
-
JDK8之前:数组+链表
-
JDK8之后:数组+链表+红黑树
以上数据结构我们称之为是哈希表
-
什么是哈希表
在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BrUtgqNR-1672475567266)(assets/哈希表.png)]
-
存储流程分析:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XWVL17u8-1672475567267)(assets/哈希流程图.png)]
-
总结
总而言之,JDK1.8引入红黑树大程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。【如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式】。
LinkedHashSet
什么是LinkedHashSet
我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序,怎么办呢?
在HashSet下面有一个子类java.util.LinkedHashSet
,它是 链表 和 哈希表 组合的一个数据存储结构。
LinkedHashSet集合的特点
-
LinkedHashSet集合中的元素不可重复
-
LinkedHashSet集合没有索引
-
LinkedHashSet集合是有序的(存储元素的顺序与取出元素顺序一致)
总结: 有序,唯一
代码演示
public class LinkedHashSetDemo {
public static void main(String[] args) {
Set<String> set = new LinkedHashSet<String>();
set.add("bbb");
set.add("aaa");
set.add("abc");
set.add("bbc");
Iterator<String> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L1M6xkGO-1672475567268)(assets/image-20201119202807088.png)]
TreeSet
使用元素的自然排序对元素进行排序
或者根据创建set时提供的Comparable排序
具体取决于你用的构造方法
TreeSet自然排序
代码实现
public class TreeSetDemo {
public static void main(String[] args) {
//使用元素的自然顺序对元素进行排序,唯一
TreeSet<Integer> ts = new TreeSet<>();
ts.add(20);
ts.add(18);
ts.add(23);
ts.add(22);
ts.add(17);
ts.add(24);
ts.add(19);
ts.add(18);
ts.add(24);
for(Integer i : ts){
System.out.println(i);
}
System.out.println("=================");
TreeSet<String> ts2 = new TreeSet<>();
ts2.add("ab");
ts2.add("e");
ts2.add("r");
ts2.add("y");
ts2.add("c");
ts2.add("ac");
for(String s : ts2){
System.out.println(s);
}
}
}
TreeSet存储自定义对象
public class Demo {
public static void main(String[] args) {
// 创建集合对象
TreeSet<Student> ts = new TreeSet<Student>();
Student s1 = new Student("b",23);
Student s2 = new Student("a",23);
Student s3 = new Student("jack",27);
ts.add(s1);
ts.add(s2);
ts.add(s3);
for(Student s : ts){
System.out.println(s.getName()+"--"+s.getAge());
}
}
}
/**
* @Auther: yanqi
* @Date: 14:40
* @Desc 如果一个类的元素要想进行自然排序,就必须实现自然排序的接口
Comparable 可以看成是内部比较器,需要修改原有代码,不符合OCP原则
*/
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student s) {
//按照年龄排序 ,主要条件
int num = this.age - s.age;//年龄相同就不存储
int num2 = num == 0 ? this.name.compareTo(s.name) : num ;//年龄相同的的时同,比较一下名是否相同
return num2;
}
}
比较器排序
Comparator 可以看成一个外部比较器,好处不用修改原代码直接实现
代码实现
package cn.yanqi_02;
import cn.yanqi_01.Student;
import cn.yanqi_02.MyComparator;
import java.util.TreeSet;
/*
* 需求:请按照姓名的长度排序
*
* TreeSet集合保证元素排序和唯一性的原理
* 唯一性:是根据比较的返回是否是0来决定。
* 排序:
* A:自然排序(元素具备比较性)
* 让元素所属的类实现自然排序接口 Comparable
* B:比较器排序(集合具备比较性)
* 让集合的构造方法接收一个比较器接口的子类对象 Comparator
*/
public class Demo {
public static void main(String[] args) {
// 创建集合对象
// TreeSet<Student> ts = new TreeSet<Student>(); //自然排序
// public TreeSet(Comparator comparator) //比较器排序
TreeSet<Student> ts = new TreeSet<Student>(new MyComparator());
// 创建元素
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
Student s8 = new Student("linqingxia", 29);
// 添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
ts.add(s8);
// 遍历
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
public class Student{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class MyComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
// int num = this.name.length() - s.name.length();
// this -- s1
// s -- s2
// 姓名长度
int num = s1.getName().length() - s2.getName().length();
// 姓名内容
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
// 年龄
int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
return num3;
}
}
Comparable<T> 内部比较器,需要修改原代码,不符合OCP原则
重写方法: public int compareTo(T t)
Comparator 可以看成一个外部比较器,好处不用修改原代码直接实现
重写方法: public int compare(Ojbect s1, Ojbect s2)
返回值类型:int 等于0 表示相等 大于0表示升序 小于0表示是降序
Map集合
什么是Map集合
现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java提供了专门的集合类用来存放这种对象关系的对象,即java.util.Map
接口。
我们通过查看Map
接口描述,发现Map
接口下的集合与Collection
接口下的集合,它们存储数据的形式不同。
Map与Collection集合区别
-
Collection集合
单列集合,一次只能添加一个元素 有的是有索引,有的没有索引 有的集合可以存储重复的元素,有的则不可以 有的元素是无序的,有的是有序的
-
Map集合
Map集合是双列集合,由Key和Value组成 Key是不允许重复的,Value是允许重复 Key允许存null值的,但是只能存储唯一的一个
Map集合中常用的子类
-
HashMap
存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
-
LinkedHashMap
HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
Map接口中常用的方法
方法名 | 说明 |
---|---|
public V put(K key, V value) | 把指定的键与指定的值添加到Map集合中。 |
public V remove(Object key) | 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。 |
public V get(Object key) | 根据指定的键,在Map集合中获取对应的值。 |
boolean containsKey(Object key) | 判断集合中是否包含指定的键。 |
public Set keySet() | 获取Map集合中所有的键,存储到Set集合中。 |
public Set<Map.Entry<K,V>> entrySet() | 获取到Map集合中所有的键值对对象的集合(Set集合)。 |
代码演示
public class MapDemo {
public static void main(String[] args) {
//创建 map对象
HashMap<String, String> map = new HashMap<String, String>();
//添加元素到集合
map.put("黄晓明", "杨颖");
map.put("文章", "马伊琍");
map.put("邓超", "孙俪");
System.out.println(map);
//String remove(String key)
System.out.println(map.remove("邓超"));
System.out.println(map);
// 想要查看 谁是谁的对象
System.out.println(map.get("黄晓明"));
System.out.println(map.get("邓超"));
System.out.println(map.get("yiyan"));//找不到返回null
}
}
注意事项
使用put方法时,若指定的键(key)在集合中没有,则没有这个键对应的值,返回null,并把指定的键值添加到集合中;
若指定的键(key)在集合中存在,则返回值为集合中键对应的值(该值为替换前的值),并把指定键所对应的值,替换成指定的新值。
Map集合的遍历
keySet
即通过元素中的键,获取键所对应的值
-
分析步骤
1. 获取Map中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。方法提示:keyset() 2. 遍历键的Set集合,得到每一个键。 3. 根据键,获取键所对应的值。方法提示:get(K key)
-
代码演示
public class MapDemo01 { public static void main(String[] args) { //创建Map集合对象 HashMap<String, String> map = new HashMap<String,String>(); //添加元素到集合 map.put("胡歌", "霍建华"); map.put("郭德纲", "于谦"); map.put("薛之谦", "大张伟"); //获取所有的键 获取键集 Set<String> keys = map.keySet(); // 遍历键集 得到 每一个键 for (String key : keys) { //key 就是键 //获取对应值 String value = map.get(key); System.out.println(key+"的CP是:"+value); } } }
EntrySet
-
什么是Entry
Map
中存放的是两种对象,一种称为key(键),一种称为value(值),它们在在Map
中是一一对应关系,这一对对象又称做Map
中的一个Entry(项)
。Entry
将键值对的对应关系封装成了对象。即键值对对象,这样我们在遍历Map
集合时,就可以从每一个键值对(Entry
)对象中获取对应的键与对应的值。 -
获取Entry
Map集合中通过
entrySet()
方法获取Entry对象public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)。
-
Entry对象中的常用方法
既然Entry表示了一对键和值,那么也同样提供了获取对应键和对应值得方法
方法名 说明 public K getKey() 获取Entry对象中的键。 public V getValue() 获取Entry对象中的值。 -
Entry图解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z1ammRlU-1672475567269)(assets/Map集合遍历方式二.bmp)]
-
代码实现
-
步骤分析
1. 获取Map集合中,所有的键值对(Entry)对象,以Set集合形式返回。方法提示:entrySet() 2. 遍历包含键值对(Entry)对象的Set集合,得到每一个键值对(Entry)对象。 3. 通过键值对(Entry)对象,获取Entry对象中的键与值。 方法提示:getkey() getValue()
-
代码实现
public class MapDemo02 { public static void main(String[] args) { // 创建Map集合对象 HashMap<String, String> map = new HashMap<String,String>(); // 添加元素到集合 map.put("胡歌", "霍建华"); map.put("郭德纲", "于谦"); map.put("薛之谦", "大张伟"); // 获取 所有的 entry对象 entrySet Set<Entry<String,String>> entrySet = map.entrySet(); // 遍历得到每一个entry对象 for (Entry<String, String> entry : entrySet) { // 解析 String key = entry.getKey(); String value = entry.getValue(); System.out.println(key+"的CP是:"+value); } } }
-
补充另外两种遍历方式
System.out.println("---------通过iterator-------------"); Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator(); while (it.hasNext()){ //返回的 key value Map.Entry<Integer, String> next = it.next(); Integer key = next.getKey(); String value = next.getValue(); System.out.println(key+": "+value); } System.out.println("----------jdk1.8新特性------------"); map.forEach((key, value) -> { System.out.println(key+": "+value); });
-
Map存储自定义类型元素
-
需求
每位学生(姓名,年龄)都有自己的家庭住址。那么,既然有对应关系,则将学生对象和家庭住址存储到map集合中。学生作为键, 家庭住址作为值。 需要: 学生姓名相同并且年龄相同视为同一名学生。
-
编写学生类
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return age == student.age && Objects.equals(name, student.name); } @Override public int hashCode() { return Objects.hash(name, age); } }
-
编写测试类
public class HashMapTest { public static void main(String[] args) { //1,创建Hashmap集合对象。 Map<Student,String>map = new HashMap<Student,String>(); //2,添加元素。 map.put(newStudent("lisi",28), "上海"); map.put(newStudent("wangwu",22), "北京"); map.put(newStudent("zhaoliu",24), "成都"); map.put(newStudent("zhouqi",25), "广州"); map.put(newStudent("wangwu",22), "南京"); //3,取出元素。键找值方式 Set<Student>keySet = map.keySet(); for(Student key: keySet){ Stringvalue = map.get(key); System.out.println(key.toString()+"....."+value); } } }
-
总结
- 当给HashMap中存放自定义对象时,如果自定义对象作为key存在,这时要保证对象唯一,必须复写对象的hashCode和equals方法(如果忘记,请回顾HashSet存放自定义对象)。
- 如果要保证map中存放的key和取出的顺序一致,可以使用
java.util.LinkedHashMap
集合来存放。
LinkedHashMap
什么是LinkedHashMap
我们知道HashMap保证成对元素唯一,并且查询速度很快,可是成对元素存放进去是没有顺序的,那么我们要保证有序,还要速度快怎么办呢?我们就可以使用LinkedHashMap
LinkedHashMap的特点
-
有序的,而且key不允许重复
-
数据结构: 哈希表 + 链表
总结:有序,key唯一
代码演示
public class LinkedHashMapDemo {
public static void main(String[] args) {
LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
map.put("邓超", "孙俪");
map.put("李晨", "范冰冰");
map.put("刘德华", "朱丽倩");
Set<Entry<String, String>> entrySet = map.entrySet();
for (Entry<String, String> entry : entrySet) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VmoNHF81-1672475567270)(assets/image-20201120101547482.png)]
TreeMap
TreeMap类概述
键是红黑树结构,可以保证键的排序和保证唯一
public static void main(String[] args) {
// 创建集合对象 会对key进行排序,并且唯一
TreeMap<String, String> tm = new TreeMap<String, String>();
// 创建元素并添加元素
tm.put("a", "你好");
tm.put("c", "世界");
tm.put("e", "爪哇");
tm.put("b", "世界2");
tm.put("e", "爪哇EE");
// 遍历集合
Set<String> set = tm.keySet();
for (String key : set) {
String value = tm.get(key);
System.out.println(key + "---" + value);
}
}
Map集合案例
需求
计算一个字符串中每个字符出现次数。要求结果的格式: a(5)b(4)c(3)d(2)e(1)
代码实现
/*
* 需求 :"aababcabcdabcde",获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1)
*
* 分析:
* A:定义一个字符串(可以改进为键盘录入)
* B:定义一个TreeMap集合
* 键:Character
* 值:Integer
* C:把字符串转换为字符数组
* D:遍历字符数组,得到每一个字符
* E:拿刚才得到的字符作为键到集合中去找值,看返回值
* 是null:说明该键不存在,就把该字符作为键,1作为值存储
* 不是null:说明该键存在,就把值加1,然后重写存储该键和值
* F:定义字符串缓冲区变量
* G:遍历集合,得到键和值,进行按照要求拼接
* H:把字符串缓冲区转换为字符串输出
*
* 录入:linqingxia
* 结果:result:a(1)g(1)i(3)l(1)n(2)q(1)x(1)
*/
public class TreeMapDemo {
public static void main(String[] args) {
// 定义一个字符串(可以改进为键盘录入)
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String line = sc.nextLine();
// 定义一个TreeMap集合
TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>();
//把字符串转换为字符数组
char[] chs = line.toCharArray();
//遍历字符数组,得到每一个字符
for(char ch : chs){
//拿刚才得到的字符作为键到集合中去找值,看返回值
Integer i = tm.get(ch);
//是null:说明该键不存在,就把该字符作为键,1作为值存储
if(i == null){
tm.put(ch, 1);
}else {
//不是null:说明该键存在,就把值加1,然后重写存储该键和值
i++;
tm.put(ch,i);
}
}
//定义字符串缓冲区变量
StringBuilder sb= new StringBuilder();
//遍历集合,得到键和值,进行按照要求拼接
Set<Character> set = tm.keySet();
for(Character key : set){
Integer value = tm.get(key);
sb.append(key).append("(").append(value).append(")");
}
//把字符串缓冲区转换为字符串输出
String result = sb.toString();
System.out.println("result:"+result);
}
}
集合的嵌套遍历
public static void main(String[] args) {
Map<String, HashMap<String, Integer>> map = new HashMap<>();
HashMap<String, Integer> map1 = new HashMap<>();
map1.put("江一燕", 33);
map1.put("yanqi", 33);
map.put("jc", map1);
HashMap<String, Integer> map2 = new HashMap<>();
map2.put("江一燕2", 33);
map2.put("yanqi2", 33);
map.put("jc2", map2);
Set<String> set = map.keySet();
for (String key : set) {
//获取所有key
System.out.println(key);
//根据key获取所有value
HashMap<String, Integer> hashMap = map.get(key);
Set<String> set2 = hashMap.keySet();
//接着再遍历value
for (String key2 : set2) {
Integer value = hashMap.get(key2);
System.out.println("\t" + key2 + ":" + value);
}
}
}
HashMap和Hashtable的区别?—面试题
/*
* 1:Hashtable和HashMap的区别?
* Hashtable:线程安全,效率低。不允许null键和null值
* HashMap:线程不安全,效率高。允许null键和null值
*
* 2:List,Set,Map等接口是否都继承子Map接口?
* List,Set不是继承自Map接口,它们继承自Collection接口
* Map接口本身就是一个顶层接口
*/
public class HashtableDemo {
public static void main(String[] args) {
// HashMap<String, String> hm = new HashMap<String, String>();
Hashtable<String, String> hm = new Hashtable<String, String>();
hm.put("it001", "hello");
// hm.put(null, "world"); //NullPointerException
// hm.put("java", null); // NullPointerException
System.out.println(hm);
}
}
模拟斗地主案例 TODO
需求
按照斗地主的规则,完成洗牌发牌的动作。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mBkKnKfJ-1672475567271)(assets/image-20201120101957523.png)]
具体规则
1. 组装54张扑克牌将
2. 54张牌顺序打乱
3. 三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。
4. 查看三人各自手中的牌(按照牌的大小排序)、底牌
注意: 手中扑克牌从大到小的摆放顺序:大王,小王,2,A,K,Q,J,10,9,8,7,6,5,4,3
需求分析
-
准备牌
完成数字与纸牌的映射关系:
使用双列Map(HashMap)集合,完成一个数字与字符串纸牌的对应关系(相当于一个字典)。
-
洗牌
通过数字完成洗牌发牌
-
发牌
将每个人以及底牌设计为ArrayList,将最后3张牌直接存放于底牌,剩余牌通过对3取模依次发牌。
存放的过程中要求数字大小与斗地主规则的大小对应。
将代表不同纸牌的数字分配给不同的玩家与底牌。
-
看牌
通过Map集合找到对应字符展示。
通过查询纸牌与数字的对应关系,由数字转成纸牌字符串再进行展示。
代码实现
package com.itfxp.doudizhu;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
/*
斗地主案例步骤:
1. 组装牌
2. 洗牌
3. 发牌 17
4. 看牌
*/
public class DDZDemo {
public static void main(String[] args) {
// 组装牌
// 牌盒
HashMap<Integer, String> poker = new HashMap<>();
// 创建集合:存储的是牌的编号
ArrayList<Integer> list = new ArrayList<>();
// 定义变量,记录牌的索引编号
int index = 2;
// 定义两个数组
// 花色
String[] colors = { "♦", "♣", "♥", "♠"};
// 数字
String[] numbers = { "2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3"};
// 遍历花色和数字数组
for (String number : numbers) {
for (String color : colors) {
String p = color + number;
poker.put(index, p);
list.add(index);
index++;
}
}
// 将大小王存储到集合中
poker.put(0, "大王");
list.add(0);
poker.put(1, "小王");
list.add(1);
// System.out.println(list);
// 洗牌
Collections.shuffle(list);
// 发牌
ArrayList<Integer> player1 = new ArrayList<>();
ArrayList<Integer> player2 = new ArrayList<>();
ArrayList<Integer> player3 = new ArrayList<>();
ArrayList<Integer> diPai = new ArrayList<>();
// 遍历ArrayList集合
for (int i = 0; i < list.size(); i++) {
if (i < 3) {
// 给底牌
diPai.add(list.get(i));
} else if (i % 3 == 0) {
// 玩家1
player1.add(list.get(i));
}else if (i % 3 == 1) {
// 玩家2
player2.add(list.get(i));
}else if (i % 3 == 2) {
// 玩家1
player3.add(list.get(i));
}
}
// 排序
Collections.sort(player1);
Collections.sort(player2);
Collections.sort(player3);
// System.out.println(player1);
// System.out.println(player2);
// System.out.println(player3);
// System.out.println(diPai);
// 看牌
lookPoker("马蓉",player1,poker);
lookPoker("王宝强",player2,poker);
lookPoker("宋吉吉",player3,poker);
lookPoker("底牌",diPai,poker);
}
public static void lookPoker(String playerName, ArrayList<Integer> list, HashMap<Integer, String> poker) {
System.out.print(playerName+"的牌是:");
for (Integer key : list) {
System.out.print(poker.get(key)+"\t");
}
System.out.println();
}
}
// 给底牌
diPai.add(list.get(i));
} else if (i % 3 == 0) {
// 玩家1
player1.add(list.get(i));
}else if (i % 3 == 1) {
// 玩家2
player2.add(list.get(i));
}else if (i % 3 == 2) {
// 玩家1
player3.add(list.get(i));
}
}
// 排序
Collections.sort(player1);
Collections.sort(player2);
Collections.sort(player3);
// System.out.println(player1);
// System.out.println(player2);
// System.out.println(player3);
// System.out.println(diPai);
// 看牌
lookPoker("马蓉",player1,poker);
lookPoker("王宝强",player2,poker);
lookPoker("宋吉吉",player3,poker);
lookPoker("底牌",diPai,poker);
}
public static void lookPoker(String playerName, ArrayList<Integer> list, HashMap<Integer, String> poker) {
System.out.print(playerName+"的牌是:");
for (Integer key : list) {
System.out.print(poker.get(key)+"\t");
}
System.out.println();
}
}