1、Map集合
Map集合的基本特点如下:
接口 Map<K,V>:将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。(但是值可以重复)
K - 此映射所维护的键的类型
V - 映射值的类型
Map集合接口的常见方法与常见子类
Map集合:该集合存储键值对。一对一对往里存。而且要保证键的唯一性。
1,添加。
put(K key, V value)
putAll(Map<? extends K,? extends V> m)
2,删除。
clear()
remove(Object key)
3,判断。
containsValue(Object value)
containsKey(Object key)
isEmpty()
4,获取。
get(Object key)
size()
values()
entrySet()
keySet()
Map
|--Hashtable:底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。jdk1.0.效率低。
|--HashMap:底层是哈希表数据结构,允许使用 null 值和 null 键,该集合是不同步的。将hashtable替代,jdk1.2.效率高。
|--TreeMap:底层是二叉树数据结构。线程不同步。可以用于给map集合中的键进行排序。
Map集合的结构和Set很像,Set底层就是使用了Map集合。
2、Map的共性方法演示
Map方法的常见方法演示如下:
package pack;
import java.util.*;
class MapDemo
{
public static void main(String[] args)
{
//我们创建一个泛型为<String,String>的map对象,比如键是学号,键对应的值是学生
Map<String,String> map = new HashMap<String,String>();//Map是接口,我们就创建其子类HashMap的对象
//添加元素,put(K key, V value):返回值类型为V
System.out.println(map.put("01", "zhangsan01"));//结果:null
System.out.println(map.put("01", "wangwu"));//结果:zhangsan01,并且01位置的值变为“wangwu”
//说明:当你往一个键里面添加值的时候,新的值会替代老的值。这里返回的V代表的是put赋值之前原来这个键所对应的值!
map.put("02", "zhangsan02");
map.put("03", "zhangsan03");
//特殊添加
map.put(null, "haha");//将键设定为null
System.out.println("获取某个键的值:"+map.get(null));//发现可以获取null键的值“haha”,说明:HashMap集合null是可以作为键存在的
map.put("04", null);//将值设定为null
//发现可以获取04键的值“null”,但是这样是没有意义的,所以,若一个键的值为null,我们判断这个键不存在(get方法)!
System.out.println("获取某个键的值:"+map.get("04"));
//判断
System.out.println("是否包含某个键:"+map.containsKey("02"));//是否包含某个键
//删除
// System.out.println("将某个键删除:"+map.remove("02"));
//获取
System.out.println("获取某个键的值:"+map.get("03"));//可以用get方法判断某个键是否存在:获取键对应的值,如果返回null说明键不存在
//获取集合中所有的值。values():返回此映射中包含的值的 Collection 视图。
Collection<String> coll = map.values();//既values返回一个Collection的集合,且集合元素类型为map值的类型。
System.out.println(coll);//获取无序的结果:哈希数据结构是无序的
//打印集合
System.out.println(map);//{01=zhangsan01, 03=zhangsan03}:发现键“02”被删除,并且其对应的值也会被删除
}
}
Map的特殊方法——将Map集合的值全部取出:keySet与entrySet方法。前面我们使用values()方法可以将map集合的值全部存放到Collection集合的对象中,这样可以获取所有的值,但是没办法得到对应键的对应值;而get()方法可以得到对应键的对应值,但是没办法得到一批键的对应值,那么现在我们来介绍其他两种方法,可以获取一批键的对应值。
下面先介绍keySet:(16-4,6.30开始有keySet方法的画图演示)
1、keySet 返回值类型Set<k> :将map中所有的键存入到Set集合。因为Set具备迭代器,而Map集合不具备迭代器。
我们可以获取Map集合所有键到Set集合中,再用迭代方式取出Set集合所有的键,再根据Map集合get方法。获取每一个键对应的值。
Map集合的取出原理:将map集合转成set集合,再通过迭代器取出。
代码示例
package pack;
import java.util.*;
class MapDemo
{
public static void main(String[] args)
{
Map<String,String> map = new HashMap<String,String>();
map.put("02", "zhangsan02");
map.put("04", "zhangsan04");
map.put("03", "zhangsan03");
map.put("01", "zhangsan01");
//首先,先获取Map集合键的Set集合
Set<String> setObj = map.keySet();
//再用迭代器将Set集合中的String对象,也就是Map集合的键取出
Iterator<String> it = setObj.iterator();//先将Set的对象取到迭代器中
while(it.hasNext())
{
String key = it.next();//将Set集合中的String对象一个一个取出,他们也是Map的键
String value = map.get(key);//根据键将map的值也取出
System.out.println("key:"+key+" value:"+value);
}
}
}
/*
结果:(视频内没有自动排序,不知道为啥这里会自动排序,可能是JDK8的新特性)
key:01 value:zhangsan01
key:02 value:zhangsan02
key:03 value:zhangsan03
key:04 value:zhangsan04
*/
下面再介绍entrySet:(16-5,6.20开始的图解释entrySet的原理)
2、Set<Map.Entry<k,v>> entrySet:将map集合中的映射关系存入到了set集合中,
而这个关系的数据类型就是:Map.Entry,既Map.Entry接口表示的就是Map中键与值的映射关系
代码示例
package pack;
import java.util.*;
class MapDemo
{
public static void main(String[] args)
{
Map<String,String> map = new HashMap<String,String>();
map.put("02", "zhangsan02");
map.put("04", "zhangsan04");
map.put("03", "zhangsan03");
map.put("01", "zhangsan01");
//将Map集合中的映射关系取出到Set集合中,映射关系的数据类型是Map.Entry<K,V>
Set<Map.Entry<String, String>> setObj = map.entrySet();
//同样设置迭代器取出映射关系的对象
Iterator<Map.Entry<String, String>> it = setObj.iterator();
while(it.hasNext())
{
//将迭代器中的每一个映射关系对象(既Map.Entey<K,V>对象)取出,放到一个变量中
Map.Entry<String, String> mapping = it.next();//返回实现Map.Entry<K,V>接口的子类的对象,类似迭代器原理
//使用Map.Entry接口的方法获取映射关系中的键与值
System.out.println("key:"+mapping.getKey()+" value:"+mapping.getValue());
}
}
}
/*
结果:(自动排序,既HashMap元素排放的顺序与元素放入HashMap时的顺序不一致,因此可以看出HashMap是无序的)(注意有序指的是元素存储顺序与集合中元素排放的顺序一致)
key:01 value:zhangsan01
key:02 value:zhangsan02
key:03 value:zhangsan03
key:04 value:zhangsan04
*/
关于Map.Entry<K,V>的解析
Entry其实就是Map中的一个static内部接口。
为什么要定义在内部呢?
因为只有有了Map集合,有了键值对,才会有键值的映射关系。既关系属于Map集合中的一个内部事物。而且该接口方法在直接访问Map集合中的元素。所以Entry接口定义在Map接口内部。
Enter接口的源代码
Map.Entry 其实Entry也是一个接口,它是Map接口中的一个内部接口。
interface Map
{
public static interface Entry
{
public abstract Object getKey();
public abstract Object getValue();
}
}
class HashMap implements Map
{
class Hahs implements Map.Entry
{
public Object getKey(){}
public Object getValue(){}
}
}
3、Map集合的练习
练习1——HashMap
/*
描述:学生Student,一个学生对应一个地址String。
学生属性:姓名,年龄,姓名和年龄相同的视为同一个学生,保证学生的唯一性。
需求:
1、描述学生;
2、定义map容器,将学生作为键,地址作为值存入;(学生作为键不能重复,而地址作为值是可以重复的!)
3、获取map集合中的元素。
*/
package pack;
import java.util.*;
class MapDemo
{
public static void main(String[] args)
{
//创建一个HashMap集合用于存储相应映射,键类型是Student,值类型是String
HashMap<Student,String> hm = new HashMap<Student,String>();
hm.put(new Student("lisi1",21),"beijing");
hm.put(new Student("lisi1",21),"tianjin");
hm.put(new Student("lisi2",22),"shanghai");
hm.put(new Student("lisi3",23),"nanjing");
hm.put(new Student("lisi4",24),"wuhan");
//第一种取出方法keySet
Set<Student> setObj1 = hm.keySet();//先用keySet方法创建一个Set集合
//构建迭代器取出键
Iterator<Student> it = setObj1.iterator();
//上面2句合并:Iterator<Student> it = hm.keySet().iterator();
while(it.hasNext())
{
Student stu = it.next();//取出每一个Student对象
String address = hm.get(stu);//通过HashMap的get(K)方法获取键值
System.out.println("姓名:"+stu.getName()+" 年龄:"+stu.getAge()+" 地址"+address);
}
System.out.println();
//第二种取出方法entrySet
Set<Map.Entry<Student, String>> setObj2 = hm.entrySet();//用entrySet()方法创建包含Map.entry<K,V>对象的Set集合
//同样创建相应迭代器并索引
Iterator<Map.Entry<Student, String>> it1 = setObj2.iterator();
while(it1.hasNext())
{
Map.Entry<Student, String> me = it1.next();//取出每一个Student对象
Student stu = me.getKey();//获取键——Student对象
String s = me.getValue();//获取值——String地址
System.out.println("姓名:"+stu.getName()+" 年龄:"+stu.getAge()+" 地址"+s);
}
}
}
//创建一个学生类继承Comparable接口,实现学生类的比较性(为了防止使用二叉树存储时无法比较,当然使用List存储的时候也可以进行比较,因为List和TreeSet一样有顺序,可以比较元素)
//且该Comparable指定的比较类型应该是Student,因为最终参与比较的应该是Student的不同对象
class Student implements Comparable<Student>
{
private String name;
private int age;
Student(String name,int age)
{
this.name = name;
this.age = age;
}
//提供获取name与age的方法
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
//重写Comparable的compareTo方法以实现比较,Comparable已经限定比较对象为Student,因此compareTo的对象只需要设定Student即可
@Override
public int compareTo(Student s) {
//先比较年龄,年龄相同再比较姓名,这种比较的先后顺序没什么区别
int num = new Integer(this.age).compareTo(new Integer(s.age));//Integer与String都有compareTo方法
//num=0,年龄相同,再比较姓名
if(num == 0)
return this.name.compareTo(s.name);
return num;
}
//涉及Hash的我们都重写hashCode与equals方法,比较需要用到!
//重写hashCode方法,使得姓名与年龄不同的Student对象哈希值不同
public int hashCode()
{
return name.hashCode()+age*23;
}
//重写equals方法,比较姓名与年龄。注意equals继承自Object,因此其参数必须为Object
public boolean equals(Object obj)
{//因为参数为Object,因此必须先判断obj是否属于Object
if(!(obj instanceof Student))
throw new ClassCastException("比较的对象不属于Student类,出错!");//抛出RuntimeException的子类ClassCastException更加准确
Student s = (Student)obj;//将obj对象向下转型为Student类型
//如果姓名年龄相同则返回true
return this.name.equals(s.name) && this.age == s.age;//String的equals用于比较内容
}
//重写toString方法
public String toString()
{
return name+":"+age;
}
}
/*
结果:不设置重复元素:结果是无序的,对应哈希表数据结构是无序的
姓名:lisi1 年龄:21 地址beijing
姓名:lisi3 年龄:23 地址nanjing
姓名:lisi2 年龄:22 地址shanghai
姓名:lisi4 年龄:24 地址wuhan
结果2:设置重复元素:没有出现重复的元素,并且lisi1出现在tianjin
我们做了hashCode()与equals()方法的复写,HashMap调用这2个方法判断前2个Student对象相同。(注意HashMap比较键也是用hashCode与equals)
Map中键不能相同,因为Student对象相同,因此后面传入的相同的Student对象就会覆盖前面的键,它指向的是tianjin的值
姓名:lisi1 年龄:21 地址tianjin
姓名:lisi3 年龄:23 地址nanjing
姓名:lisi2 年龄:22 地址shanghai
姓名:lisi4 年龄:24 地址wuhan
*/
TreeMap不会对存放的对象进行排序,但是可以通过compareTo方法比较对象是否相同。而下面的TreeMap会使用compareTo方法比较对象是否相同,并通过对象大小对对象进行排序。
练习2:TreeMap
/*
需求:我们前面实现Comparable的自然顺序是依照学生的年龄排序,对比年龄相同后再比较姓名。
我们现在想根据学生的姓名排序——先比较姓名,姓名相同再比较年龄。——利用比较器
因为数据是以键值对形式存在的,所以要使用可以排序的Map集合——TreeMap。——TreeMap会排序——Comparable或者Comparator
*/
package pack;
import java.util.*;
class MapDemo
{
public static void main(String[] args)
{
//创建一个HashMap集合用于存储相应映射,键类型是Student,值类型是String
TreeMap<Student,String> tm = new TreeMap<Student,String>(new StuNameCompare());
tm.put(new Student("blisi3",23),"nanjing");
tm.put(new Student("lisi1",21),"beijing");
tm.put(new Student("alisi4",24),"wuhan");
tm.put(new Student("lisi1",21),"tianjin");
tm.put(new Student("lisi2",22),"shanghai");
Set<Map.Entry<Student, String>> setObj = tm.entrySet();
Iterator<Map.Entry<Student, String>> it = setObj.iterator();
while(it.hasNext())
{
Map.Entry<Student, String> me = it.next();//取出每一个Student对象
Student stu = me.getKey();//获取键——Student对象
String s = me.getValue();//获取值——String地址
System.out.println("姓名:"+stu.getName()+" 年龄:"+stu.getAge()+" 地址"+s);
}
}
}
//创建一个比较器的类
class StuNameCompare implements Comparator<Student>
{
public int compare(Student s1,Student s2)
{//先比较姓名
int num = s1.getName().compareTo(s2.getName());
if(num == 0)//姓名相同再比较年龄,都一样就是同一个对象,同一个键!
return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
return num;
}
}
class Student implements Comparable<Student>
{
private String name;
private int age;
Student(String name,int age)
{
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public int compareTo(Student s) {
int num = new Integer(this.age).compareTo(new Integer(s.age));
if(num == 0)
return this.name.compareTo(s.name);
return num;
}
public int hashCode()
{
return name.hashCode()+age*23;
}
public boolean equals(Object obj)
{
if(!(obj instanceof Student))
throw new ClassCastException("比较的对象不属于Student类,出错!");
Student s = (Student)obj;
return this.name.equals(s.name) && this.age == s.age;
}
public String toString()
{
return name+":"+age;
}
}
/*
结果1:发现元素自动排序——因为学生Student实现了Comparable接口,具备自然顺序(Comparable的自然顺序是按照年龄排序)
姓名:lisi1 年龄:21 地址beijing
姓名:lisi2 年龄:22 地址shanghai
姓名:lisi3 年龄:23 地址nanjing
姓名:lisi4 年龄:24 地址wuhan
结果2:修改Student元素,并添加重复的Student元素(添加比较器Comparator)
姓名:alisi4 年龄:24 地址wuhan
姓名:blisi3 年龄:23 地址nanjing
姓名:lisi1 年龄:21 地址tianjin
姓名:lisi2 年龄:22 地址shanghai
发现先按姓名自动排序,而且相同的Student作为TreeMap的键会被后面进来的相同Student对象覆盖!
*/
练习3:TreeMap练习2
/*
练习:
"sdfgzxcvasdfxcvdf"获取该字符串中的字母出现的次数。
希望打印结果:a(1)c(2).....
分析:通过结果发现,每一个字母都有对应的次数,说明字母和次数之间都有映射关系。
注意了,当发现有映射关系时,可以选择map集合,因为map集合中存放就是映射关系。
以后当数据之间存在这映射关系时,就要先想map集合。
思路:
1、将字符串转换成字符数组,因为要对每一个字母进行操作。
2、定义一个map集合,因为打印结果的字母有顺序,所以使用treemap集合。
该集合的键为字母(Character)、集合的值为字母出现的次数
3、遍历字符数组。
将每一个字母作为键去查map集合。
如果返回null,说明该字母在之前没有存入,将该字母和1存入到map集合中。
如果返回不是null,说明该字母在map集合已经存在并有对应次数。
那么就获取该次数并进行自增,然后将该字母和自增后的次数存入到map集合中,覆盖调用原理键所对应的值。
4、将map集合中的数据变成指定的字符串形式返回。
*/
package pack;
import java.util.*;
class MapDemo
{
public static void main(String[] args)
{
String s = charCount("ak+abAf1c,dCkaAbc-defa");
System.out.println(s);
}
//创建一个方法来实现字母计数的功能:返回值为要求的字符串,输入值参数为需要计数的字符串
public static String charCount(String str)
{
//首先将需要计数的字符串放入字符数组中
char[] charArr = str.toCharArray();
//其次创建一个TreeMap集合用于存储字母以及其出现的顺序
TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>();
//集合中存放的必须是对象,因此使用Integer表示整形,这样才能将对象存放进去。
Integer timeValue = 0;//定义一个变量用于存储字母次数
//第二种插入方法
int count = 0;//定义一个临时的计数器并初始化为0
//下面根据字符数组的长度遍历该字符数组
for(int x=0; x<charArr.length ; x++)
{
//首先,如果要排序的字符串数组中的字符不是字母,那么这次循环就跳过,继续下一次循环,这种字符不需要存入
if(!((charArr[x]>='a' && charArr[x]<='z')||(charArr[x]>='A' && charArr[x]<='Z')))
continue;//跳过此处循环,进入下一次循环
//如果查询的是字母,我们检查该字母在tm中存储的出现的次数
timeValue = tm.get(charArr[x]);
//我们用另一种方法来将字母与其出现次数插入集合
if(!(timeValue == null))//若字母在集合中存储的次数不为null,将timeValue的值赋予临时计数器count;
count = timeValue;//这里Integer类型拆箱为int类型
count++;//不管timeValue之前是否为0,次数都会自加1。如果不设置count,在timeValue == null的时候,次数表示为null,而不是0次,这与我们的需求是不符的。因为null是无法进行自加操作的。
tm.put(charArr[x], count);
count = 0;//当然最后还是要将count置零,保证下一次进来的字母保存初始次数为0.
/*
if(timeValue == null)//这里注意timeValue只能为null,而不是为0,因为其是从集合中取出的,集合中元素不存在定义为null
{
tm.put(charArr[x], 1);//将该字母及其出现次数1存入tm
}
else
{
tm.put(charArr[x],timeValue+1);//将该字母及其出现value+1存入tm
}
*/
}
//循环将字母与次数存储到tm完毕后,我们遍历将其取出,并通过StringBuilder的append方法将各个字母及存入字符串中
StringBuilder sb = new StringBuilder();
//先创建Map.Entry<Character, Integer>类型的Set集合
Set<Map.Entry<Character, Integer>> entrySet = tm.entrySet();
//再创建迭代器
Iterator<Map.Entry<Character, Integer>> it = entrySet.iterator();
while(it.hasNext())
{
Map.Entry<Character, Integer> me = it.next();//先将Map.Entry<Character, Integer>对象取出
//再获取键与值
Character charKey = me.getKey();
Integer intValue = me.getValue();
//接下来将字符(键)与次数(值)添加到sb
sb.append(charKey+"("+intValue+")");
}
//最后返回sb.toString既是我们想要的字符串(StringBuilder对象提供toString将对象转换为字符串)
return sb.toString();
}
}
/*
结果:
A(2)C(1)a(4)b(2)c(2)d(2)e(1)f(2)k(2)
由于Character实现了Comparable,因此TreeSet中字母具有默认的自然排序!
*/
4、Map集合扩展
先看如下示例
/*
map扩展知识,map集合被使用是因为具备映射关系。
试着存储如下内容:有2个映射关系:
"yureban" Student("01" "zhangsan");
"yureban" Student("02" "lisi");
"jiuyeban" "01" "wangwu";
"jiuyeban" "02" "zhaoliu";
分析:班级对应学生,而学生中学号对应姓名。
有多个班级,一个班级可能对应很多学生,这是一对多映射;一个学号值对应一个姓名,这是我们之前将的一对一映射。
现在要选择处理一对多映射的方法
*/
package pack;
import java.util.*;
class MapDemo
{
public static void main(String[] args)
{
//首先创建一个HashMap集合,对预热班的学生进行描述,这里描述的是预热班中学生学号(String)与姓名(String)的一对一关系
HashMap<String,String> yure = new HashMap<String,String>();
//往预热班中添加学生
yure.put("01", "zhangsan");
yure.put("02", "lisi");
//同样创建就业班的集合
HashMap<String,String> jiuye = new HashMap<String,String>();
jiuye.put("01", "wangwu");
jiuye.put("02", "zhaoliu");
//接下来,我们对班级与学生之间进行描述,由于有多个班级,且一个班级对应多个学生
//那么我们创建班级名称(String)与存储多个学生实体的班级集合的HashMap集合(16-9,7.30处解析)
HashMap<String,HashMap<String,String>> czbk = new HashMap<String,HashMap<String,String>>();
//添加班级名称与班级集合
czbk.put("yure", yure);
czbk.put("jiuye", jiuye);
//接下来,我们创建Set集合与迭代器遍历czdb集合
Set<String> keySet = czbk.keySet();
Iterator<String> it = keySet.iterator();
while(it.hasNext())
{
//获取String类型的键,既班级名称
String className = it.next();
//通过键获取值,既班级
HashMap<String,String> classValue = czbk.get(className);
System.out.println("班级名称为:"+className);
//再通过getStudentInfo()方法遍历班级集合中的元素
MapDemo.getStudentInfo(classValue);
}
}
//创建遍历班级集合的方法
public static void getStudentInfo(HashMap<String,String> map)
{
Set<Map.Entry<String, String>> entrySet = map.entrySet();
Iterator<Map.Entry<String, String>> it = entrySet.iterator();
while(it.hasNext())
{//获取Map.Entry<String, String>类型的对象
Map.Entry<String, String> me = it.next();
String id = me.getKey();
String name = me.getValue();
System.out.println("学号:"+id+" 姓名"+name);
}
}
}
/*
结果:成功遍历!
班级名称为:jiuye
学号:01 姓名wangwu
学号:02 姓名zhaoliu
班级名称为:yure
学号:01 姓名zhangsan
学号:02 姓名lisi
*/
我们将上面的例子进行优化
/*
我们将学生封装成为Student类来描述,由于学号有顺序,且每一个学生Student都不能重复,这样将其设置为HashMap类型,
这样作为HashMap的值(可重复)不合适,那么我们用List来存储Student对象(16-9.19.30开始解析)
"yureban" Student("01" "zhangsan");
"yureban" Student("02" "lisi");
"jiuyeban" "01" "wangwu";
"jiuyeban" "02" "zhaoliu";
*/
package pack;
import java.util.*;
class MapDemo
{
public static void main(String[] args)
{
MapDemo.printInfo();//打印所有信息
}
//创建一个遍历打印所有班级与学生信息的方法
public static void printInfo()
{
//同样先创建学校HashMap集合
HashMap<String,List<Student>> czbk = new HashMap<String,List<Student>>();
//创建班级List集合,其为Student类型
List<Student> yure = new ArrayList<Student>();
List<Student> jiuye = new ArrayList<Student>();
//添加班级
czbk.put("yure", yure);
czbk.put("jiuye", jiuye);
//为各个班级添加学生
yure.add(new Student("01","zhangsan"));
yure.add(new Student("02","lisi"));
jiuye.add(new Student("01","wangwu"));
jiuye.add(new Student("02","zhaoliu"));
MapDemo.getClassInfo(czbk);//遍历班级名与各个班级实体
}
//我们创建获取学校HashMap集合班级信息的方法
public static void getClassInfo(HashMap<String,List<Student>> sch)
{
Iterator<String> it = sch.keySet().iterator();
while(it.hasNext())
{
//获取键,既班级名称
String className = it.next();
//获取班级实体,为List<Student>类型
List<Student> classValue = sch.get(className);
System.out.println("班级:"+className);
MapDemo.getStudentInfo(classValue);//遍历班级内的学生学号与姓名
}
}
//我们再创建获取班级List集合中学号与姓名信息的方法
public static void getStudentInfo(List<Student> classValue)
{
Iterator<Student> it = classValue.iterator();
while(it.hasNext())
{
//获取学生对象
Student stu = it.next();
//打印学生信息
System.out.println("学号:"+stu.getId()+" 姓名"+stu.getName());
}
}
}
//Student类
class Student
{
private String id;
private String name;
Student(String id,String name)
{
this.id = id;
this.name = name;
}
public String getId()
{
return id;
}
public String getName()
{
return name;
}
}
/*
结果:成功遍历!
班级:jiuye
学号:01 姓名wangwu
学号:02 姓名zhaoliu
班级:yure
学号:01 姓名zhangsan
学号:02 姓名lisi
*/
4、Map集合补充
补充1:
HashMap集合的特点
HashMap集合的特点:
1.HashMap集合底层是哈希表:查询的速度特别的快
JDK1.8之前:数组+单向链表
JDK1.8之后:数组+单向链表|红黑树(链表的长度超过8):提高查询的速度
2.hashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致
java.util.LinkedHashMap<k,v>集合 extends HashMap<k,v>集合
LinkedHashMap的特点:
1.LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序)
2.LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的
补充2:
Map.Entry接口的解析(见就业班Map接口-05)
补充3:
HashMap存储自定义类型键值(见就业班Map接口-07)
Map集合保证key是唯一的:作为key的元素,必须重写hashCode方法和equals方法,以保证key唯一。
底层在添加的时候会调用这两个方法,判断要添加的key值对象是否与之前添加过的对象相同,不同则存储,反之不存储。
Hash结构的集合调用equals与hashCode方法,以比较存储的对象是否相同。
补充4:
有序版本的斗地主
package lkj.demo1;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
public class DouDiZhu
{
/*
斗地主综合案例:有序版本
1.准备牌
2.洗牌
3.发牌
4.排序
5.看牌
*/
public static void main(String[] args)
{
//1.准备牌
//创建一个Map集合,存储牌的索引和组装好的牌
HashMap<Integer,String> poker = new HashMap<>();
//创建一个List集合,存储牌的索引
ArrayList<Integer> pokerIndex = new ArrayList<>();
//定义两个集合,存储花色和牌的序号
List<String> colors = new ArrayList<>();
List<String> numbers = new ArrayList<>();
//使用Collections的addAll方法往ArrayList集合中添加元素.
//这里没办法使用JDK9的of方法。只能使用Collections方法addAll方法替代
Collections.addAll(colors,"♠", "♥", "♣", "♦");
Collections.addAll(numbers,"2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3");
//把大王和小王存储到集合中
//定义一个牌的索引
int index = 0;
poker.put(index,"大王");
pokerIndex.add(index);
index++;
poker.put(index,"小王");
pokerIndex.add(index);
index++;
//循环嵌套遍历两个集合,组装52张牌,存储到集合中
for(String col:colors)
{
for(String num:numbers)
{
poker.put(index,col+num);
pokerIndex.add(index);
index++;
}
}
// System.out.println(poker);
/*
2.洗牌
使用Collections中的方法shuffle(List)
*/
Collections.shuffle(pokerIndex);//将索引集合的顺序打乱
/*
3.发牌
*/
//定义4个集合,存储玩家牌的索引,和底牌的索引
ArrayList<Integer> player01 = new ArrayList<>();
ArrayList<Integer> player02 = new ArrayList<>();
ArrayList<Integer> player03 = new ArrayList<>();
ArrayList<Integer> diPai = new ArrayList<>();
//遍历存储牌索引的List集合,获取每一个牌的索引
for (int i = 0; i < pokerIndex.size(); i++)
{
Integer in = pokerIndex.get(i);//获取索引集合pokerIndex的值,并将其赋予每一个玩家的集合
if(i>=51)
{
diPai.add(in);
}
if(i%3 == 0)
{
player01.add(in);
}
else if(i%3 == 1)
{
player02.add(in);
}
else if(i%3 == 2)
{
player03.add(in);
}
}
/*
4.排序
使用Collections中的方法sort(List)
默认是升序排序
*/
Collections.sort(player01);
Collections.sort(player02);
Collections.sort(player03);
Collections.sort(diPai);
/*
5.看牌
调用看牌的方法
*/
lookPoker("刘德华",poker,player01);
lookPoker("周润发",poker,player02);
lookPoker("周星驰",poker,player03);
lookPoker("底牌",poker,diPai);
}
/*
定义一个看牌的方法,提高代码的复用性
参数:
String name:玩家名称
HashMap<Integer,String> poker:存储牌的poker集合
ArrayList<Integer> list:存储玩家和底牌的List集合
查表法:
遍历玩家或者底牌集合,获取牌的索引
使用牌的索引,去Map集合中,找到对应的牌
*/
public static void lookPoker(String name , HashMap<Integer,String> poker, ArrayList<Integer> list)
{
//输出玩家名称,不换行
System.out.print(name+":");
//遍历玩家或者底牌集合,获取牌的索引——需要注意的是,这里必须通过list的Integer元素来查找poker中的元素
for (int i=0 ; i<list.size() ;i++)
{
//使用牌的索引,去Map集合中,找到对应的牌
String value = poker.get(list.get(i));
System.out.print(value+" ");
}
System.out.println();//打印完每一个玩家的牌,换行
}
}