黑马毕向东Java课程笔记(day16-1-16-9):集合类(集合框架)——Map集合

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();//打印完每一个玩家的牌,换行
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值