一 Map概述
1、初步认识Map集合
Map和Collection一样,也是属于 Java Collections Framework的顶层接口。
首先看API文档的介绍:
接口 Map<K,V> — 其中 “K代表Key(键),V代表Value(值)”,
Map对象每次能存键、值两个元素,键值间有映射关系,
一个映射不能包含重复的键;每个键最多只能映射到一个值。
K — 此映射所维护的键的类型
V — 映射值的类型
Map集合存储键值对是 一对一对往里存,而且要保证键的唯一性。
2、Map集合的“增删改查”
|---- 添加
|---- put(K key,V value)将指定的值与此映射中的指定键关联(可选操作)。
|----putAll(Map<? extends K,? extends V> m) 从指定映射中将所有映射关系复制到此映射中(可选操作)。
|---- 删除
|----clear() 清空
|----remove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
|---- 判断
|----containsKey(Object key) 是否包含某个key
|----containsValue(Object value) 是否包含某个value
|----isEmpty()如果此映射未包含键-值映射关系,则返回true。
|---- 获取
|----get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回null
。
|----int size()返回此映射中的键-值映射关系数。
|----Collection<V> values()返回此映射中包含的值的 Collection 视图。
|----Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图。
|----Set<K> keySet()返回此映射中包含的键的 Set 视图。
二 Map子类对象特点
我们要了解Map集合的三个子类对象,Hashtable、HashMap、TreeMap,后两个比较常见。
|---- Map
|---- Hashtable:底层是哈希表数据结构,不可以存入null键null值。(JDK1.0出现的,线程同步)
此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。
为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。
|---- HashMap: 底层是哈希表数据结构, 此实现提供所有可选的映射操作,允许使用null键和null值,该集合不同步。(JDK1.2出现,效率更高)
(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)
HashMap不保证映射的顺序,特别是它不保证该顺序一直不变。
|---- TreeMap: 底层是二叉树数据结构,线程不同步。可以用于给Map集合中的键进行排序。
TreeMap根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
我们可以发现,Map集合和Set集合很类似,这是因为Set底层就使用了Map集合。
另外,面试中“Hashtable和HashMap有什么区别”这个问题经常会被问到,请留意。
三 Map共性方法
概述中已经提到,这里简单以代码说明:
import java.util.*;
public class TreeMapDemo {
public static void main(String[] args) {
Map<String, String> tm = new HashMap<String, String>();
// 添加元素
tm.put("12", "AAA");
tm.put("13", "ABA");
tm.put("12", "ACA");
tm.put("22", "AAD");
tm.put(null, "NULL1");
tm.put(null, "NULL2");
tm.put("16", null);
// 看“12”那个键。添加元素,如果出现相同的键。那么后面添加的值会覆盖原有键对应的值。
// put方法会返回被覆盖的值。
System.out.println(tm.size()); // Key为null的映射关系是不算在里面的。
System.out.println(tm.containsKey("12")); // true
System.out.println(tm.containsValue("ABA")); // true
System.out.println(tm.get("12")); // ACA
// 通过get方法的返回值来判断一个键是否存在。通过返回null来判断
System.out.println(tm.get("null")); // 不是返回NULL1或者NULL2,总是返回null
// values()方法:获取map集合中所有的值。
Collection<String> coll = tm.values();
System.out.println(coll); // //{null=NULL2, 22=AAD, 16=null, 13=ABA}
System.out.println(tm); // {null=NULL2, 22=AAD, 16=null, 13=ABA, 12=ACA}
System.out.println(tm.remove("12")); // ACA,注意返回的是被移除那个键的值。
System.out.println(tm); // {null=NULL2, 22=AAD, 16=null, 13=ABA}
}
}
输出:
5
true
true
ACA
null
[NULL2, AAD, null, ABA, ACA]
{null=NULL2, 22=AAD, 16=null, 13=ABA, 12=ACA}
ACA
{null=NULL2, 22=AAD, 16=null, 13=ABA}
四 Map -KeySet
Map集合取出里面的对象,有两种方式(就是键、键值对各对应一种):KeySet()和entrySet()。
先了解KeySet,API文档说:“Set<K> keySet() 返回此映射中包含的键的 Set 视图。”
也就是:将map中所有的键存入到Set集合。
因为Set有迭代器,所以可以用迭代方式取出所有的键,再根据get方法。获得每一个键对应的值。
示例代码:
import java.util.*;
public class TreeMapDemo {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("12", "AAA");
map.put("13", "ABA");
map.put("12", "ACA");
map.put("22", "AAD");
// 先获取map集合的所有键的Set集合,keySet()
Set<String> theMapSet = map.keySet();
// 有了Set集合,就可以获取其迭代器。
Iterator<String> it = theMapSet.iterator();
while (it.hasNext()) {
// 有了键可以通过map集合的get方法获取其对应的值
String key = it.next();
System.out.println("Key : " + key + " Value : " + map.get(key));
}
}
}
输出:
Key : 22 Value : AAD
Key : 13 Value : ABA
Key : 12 Value : ACA
这种keySet()的取出方式,是将Map集合转为Set集合,然后通过迭代器取出。
五 Map - entrySet
1、认识entrySet()方法
现在介绍另外一种取出方式,entrySet()。
先看API文档:
可以看出,entrySet()将Map集合中的映射关系存入到了set集合中,而这个关系的数据类型就是:Map.Entry。
Map.Entry<K,V>:映射项(键-值对)。再看Map.Entry<K,V>接口的方法:
那么关系对象 Map.Entry 获取到后,就可以通过,Map.Entry中getKey()和getValue()方法获取关系中的键和值
(Map.Entry 其实Entry也是一个接口,她是接口中的一个内部接口)
比喻:就相当于把“结婚证书”这样的关系取出来了,所以既不是丈夫类型也不是妻子类型,而是夫妻关系(Map.Entry)类型。
2、示例代码
import java.util.*;
public class TreeMapDemo {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("12", "AAA");
map.put("13", "ABA");
map.put("12", "ACA");
map.put("22", "AAD");
// 将Map集合中的映射关系取出,存入到Set集合中。
Set<Map.Entry<String, String>> entrySet = map.entrySet(); // 将Map集合中的映射关系取出,这个关系是Map.Entry类型
Iterator<Map.Entry<String, String>> it = entrySet.iterator();
while (it.hasNext()) {
Map.Entry<String, String> me = it.next();
String key = me.getKey(); // 那么关系对象Map.Entry获取到后
String value = me.getValue(); // ,就可以通过Map.Entry中getKey和getValue方法获取关系中的键和值
System.out.println(key + ":" + value);
}
}
}
/*
* Map.Entry其实Entry也是一个接口,它是Map接口中的一个内部接口。
*/
/*
* interface Map{ public static interface Entry{ //有了Map集合才有关系,所以Entry是内部接口
* public abstract Object getKey();
* public abstract Object getValue();
* }
* }
*/
输出:
22:AAD
13:ABA
12:ACA
六 Map练习
/*
每一个学生都有对应的归属地
学生Student,地址String
学生属性:姓名,年龄
注意:姓名和年龄相同的视为同一个学生。
保证学生的唯一性
1、描述学生
2、定义Map容器,将学生作为键,地址作为值,存入
3、获取map集合中元素
*/
import java.util.*;
public class Demo {
public static void main(String[] args) {
HashMap<Student, String> map = new HashMap<Student, String>();
map.put(new Student("lisi1", 21), "beijing");
map.put(new Student("lisi2", 22), "shanghai");
map.put(new Student("lisi3", 23), "nanjing");
map.put(new Student("lisi4", 24), "wuhan");
sop("第一种");
Set<Student> setMap = map.keySet();
Iterator<Student> it = setMap.iterator();
while (it.hasNext()) {
Student st = it.next();
sop("name:" + st.getName() + " value:" + st.getAge());
}
sop("第二种");
Map<Student, String> map2 = new HashMap<Student, String>();
map2.put(new Student("lisi1", 21), "beijing");
map2.put(new Student("lisi2", 22), "shanghai");
map2.put(new Student("lisi3", 23), "nanjing");
map2.put(new Student("lisi4", 24), "wuhan");
// 将Map集合中的映射关系取出。存入到Set集合中。
Set<Map.Entry<Student, String>> entrySet = map2.entrySet();
Iterator<Map.Entry<Student, String>> it2 = entrySet.iterator();
while (it2.hasNext()) {
Map.Entry<Student, String> me = it2.next();
sop("name:" + me.getKey().getName() + " value:"
+ me.getKey().getAge() + " address:" + me.getValue());
}
}
public static void sop(Object obj) {
System.out.println(obj);
}
}
class Student implements Comparable {
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 String toString() {
return name + ":" + age;
}
public int hashCode() {
return name.hashCode() + age * 34;
}
public boolean equals(Object obj) {
if ((obj instanceof Student))
throw new ClassCastException("类型不匹配");
Student s = (Student) obj;
return this.name.equals(s.name) && this.age == s.age;
}
public int compareTo(Object obj) {
Student s = (Student) obj;
int num = new Integer(this.age).compareTo(new Integer(s.age));
if (num == 0)
return this.name.compareTo(s.name);
return num;
}
}
七 TreeMap练习
练习:对学生对象的年龄进行升序排序
/*
需求:对学生对象的年龄进行升序排序
因为数据是以键值对形式存在的
所以要使用可以排序的Map集合,TreeMap
*/
import java.util.*;
public class Demo {
public static void main(String[] args) {
Map<Student, String> map = new TreeMap<Student, String>();
map.put(new Student("lisi1", 23), "beijing");
map.put(new Student("lisi2", 22), "shanghai");
map.put(new Student("lisi3", 21), "nanjing");
map.put(new Student("lisi4", 24), "wuhan");
// 将Map集合中的映射关系取出。存入到Set集合中。
Set<Map.Entry<Student, String>> entrySet = map.entrySet();
Iterator<Map.Entry<Student, String>> it = entrySet.iterator();
while (it.hasNext()) {
Map.Entry<Student, String> me = it.next();
sop("name:" + me.getKey().getName() + " value:"
+ me.getKey().getAge() + " address:" + me.getValue());
}
}
public static void sop(Object obj) {
System.out.println(obj);
}
}
class Student implements Comparable {
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 String toString() {
return name + ":" + age;
}
public int hashCode() {
return name.hashCode() + age * 34;
}
public boolean equals(Object obj) {
if ((obj instanceof Student))
throw new ClassCastException("类型不匹配");
Student s = (Student) obj;
return this.name.equals(s.name) && this.age == s.age;
}
public int compareTo(Object obj) {
Student s = (Student) obj;
int num = new Integer(this.age).compareTo(new Integer(s.age));
if (num == 0)
return this.name.compareTo(s.name);
return num;
}
}
输出:
name:lisi3 value:21 address:nanjing
name:lisi2 value:22 address:shanghai
name:lisi1 value:23 address:beijing
name:lisi4 value:24 address:wuhan
八 TreeMap练习 - 字母出现的次数
"sdfgzxcvasdfxcvdf"获取该字符串中的字母出现的次数
希望打印结果 a(1)c(2)
思考:
通过结果发现,每一个字母都有对应的次数,说明字母和次数之间都有映射关系
注意,当发现有映射关系时,可以选择map集合。因为map集合中存放的就是映射关系。
什么时候使用map集合呢?当数据之间存在这种映射关系时候,就要先想到map集合。
思路:
(1) 将字符串转换成字符数组,因为要对每一个字母进行操作
(2) 定义一个map集合,因为打印结果的字母有顺序,所以使用TreeMap集合
(3) 遍历字符数组,
- 将每一个字母作为键去查map集合
- 如果返回null,将该字母和1存入到集合中
- 如果返回不是null,说明该字母在map集合已存在,并有已存在的次数,那么获取该次数并自增,
- 然后将该字母和自增后的次数存入到map集合中,覆盖掉原键所对应的值
(4) 将map集合中的数据编程指定的字符串形式返回
import java.util.*;
public class Demo {
public static void main(String[] args) {
String sb = charCount("aabfcdabcdefa");
sop(sb);
}
public static String charCount(String str) {
char[] chs = str.toCharArray();
TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>();
for (int x = 0; x < chs.length; x++) {
if (!(chs[x] >= 'a' && chs[x] <= 'z' || chs[x] >= 'A'
&& chs[x] <= 'Z'))
continue;
Integer value = tm.get(chs[x]);
int count = 0;
if (value != null)
count = value;
count++;
tm.put(chs[x], count);
}
StringBuilder sb = new StringBuilder();
Set<Map.Entry<Character, Integer>> entryset = new TreeSet<Map.Entry<Character, Integer>>(
new myCompare());
entryset = tm.entrySet();
Iterator<Map.Entry<Character, Integer>> it = entryset.iterator();
while (it.hasNext()) {
Map.Entry<Character, Integer> me = it.next();
Character ch = me.getKey();
Integer value = me.getValue();
sb.append(ch + "(" + value + ") ");
}
return sb.toString();
}
public static void sop(Object obj) {
System.out.println(obj);
}
}
class myCompare implements Comparator {
public int compare(Object o1, Object o2) {
return 1;
// o1 = (Map.Entry)o1;
// o2 = (Map.Entry)o2;
// int value =
// (Integer)(o1.getValue()).compareTo((Integer)(o2.getValue()));
// if(value==0){
// value = (Character)(o1.getKey()).compareTo((Character)(o2.getKey()));
// }
}
}
输出:a(4) b(2) c(2) d(2) e(1) f(2)
九 Map扩展
import java.util.*;
public class Demo {
public static void main(String[] args) {
FirstMethod();
AnotherMethod();
}
public static void FirstMethod() {
HashMap<String, HashMap<String, String>> school = new HashMap<String, HashMap<String, String>>();
HashMap<String, String> yure = new HashMap<String, String>(); // 预热班
HashMap<String, String> jiuye = new HashMap<String, String>();// 就业班
yure.put("01", "zhangsan");
yure.put("02", "lisi");
jiuye.put("01", "wangwu");
jiuye.put("02", "zhaoliu");
school.put("yure", yure);
school.put("jiuye", jiuye);
// 取出某个班级的学生
// getStudentInfo(yure);
// 遍历school集合。获取所有的教室。
Iterator<String> it = school.keySet().iterator();
while (it.hasNext()) {
String roomName = it.next();
sop(roomName);
getStudentInfo1(school.get(roomName)); // 其实就是循环嵌套
}
}
public static void getStudentInfo1(HashMap<String, String> roomMap) {
Iterator<String> it = roomMap.keySet().iterator();
while (it.hasNext()) {
String id = it.next();
String name = roomMap.get(id);
System.out.println(id + ":" + name);
}
}
public static void AnotherMethod() {
HashMap<String, List<Student>> school = new HashMap<String, List<Student>>();
List yure = new ArrayList<Student>(); // 预热班
List jiuye = new ArrayList<Student>();// 就业班
school.put("yure", yure);
school.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"));
Iterator<String> it = school.keySet().iterator();
while (it.hasNext()) {
String roomName = it.next();
List<Student> room = school.get(roomName);
System.out.println(roomName);
getStudentInfo2(room);
}
}
public static void getStudentInfo2(List<Student> list) {
Iterator<Student> it = list.iterator();
while (it.hasNext()) {
Student s = it.next();
System.out.println(s.getId() + " : " + s.getName());
}
}
public static void sop(Object obj) {
System.out.println(obj);
}
}
class Student {
private String id;
private String name;
public Student(String id, String name) {
super();
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
通过上面的例子了解map的扩展知识,map之所以结合被使用,是因为具备映射关系。
假设我们学校有“预热班”和“就业班”,两个班又有自己从“01”学号开始的学生,那么用map怎么存呢?
因为学生本身有"学号-姓名"的映射,"班级-学生-"又有映射关系,
所以这里不同以往,有两个映射关系,因为集合本身也是对象,
所以我们可以将"学生"映射关系作为值传入"班级-学生-"的关系中。
也就是:map("所在班级",“学号-姓名”);这样的map。
以此实现了FirstMethod()那样的代码,之后再改进为AnotherMethod()那样的代码。