Map接口的概述
将键映射到值的对象,一个映射不能包含重复的键,每个键最多只能映射到一个值。其实Map集合是一个双列集合,存储的就是键值对,而且Map集合中必须保证键的唯一性。
Map接口的特点
- Map与Collection在集合框架中属并列存在,而且Map是双列的,Collection是单列的;
- Map的键唯一,Collection的子体系Set是唯一的;
- Map集合的数据结构只针对键有效,跟值无关;Collection集合的数据结构是针对元素有效。
Map接口中的常用方法
下面我们就来演示以上的一些方法。
package cn.liayun.map;
import java.util.HashMap;
import java.util.Map;
public class MapDemo {
public static void main(String[] args) {
/*
* 需求:在Map集合中存储学号,姓名。
*/
Map<Integer, String> map = new HashMap<Integer, String>();
methodDemo(map);
}
public static void methodDemo(Map<Integer, String> map) {
//1,存储键值对。如果键相同,会出现值覆盖。
System.out.println(map.put(3, "xiaoqiang"));
System.out.println(map.put(3, "erhu"));
map.put(7, "wangcai");
map.put(2, "daniu");
// System.out.println(map.remove(7));
System.out.println(map.get(7));
System.out.println(map);
}
}
Map接口的常见子类
Map和Set很像,其实Set底层就是使用了Map集合。
Map集合的三种取出所有元素的方式
Map集合的三种取出所有元素的方式:
Map集合的取出所有元素的第一种方式——keySet()方法
下面我们就来演示Map集合的第一种取出方式。
package cn.liayun.map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapDemo2 {
public static void main(String[] args) {
/*
* 取出Map中所有的元素。
* Map中存储姓名---归属地
*/
Map<String, String> map = new HashMap<String, String>();
map.put("xiaoqiang", "beijing");
map.put("wangcai", "funiushan");
map.put("daniu", "heifengzhai");
map.put("erhu", "wohudong");
map.put("zhizunbao", "funiushan");
//keySet(),取出所有的键,并存储到Set集合中。map集合没有迭代器,但是可以将map集合转成Set集合,再使用迭代器就哦了。
Set<String> keySet = map.keySet();
for (Iterator<String> it = keySet.iterator(); it.hasNext();) {
String key = it.next();
String value = map.get(key);
System.out.println(key + ":" + value);
}
}
}
Map集合的取出所有元素的第二种方式——entrySet()方法
package cn.liayun.map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapDemo2 {
public static void main(String[] args) {
/*
* 取出Map中所有的元素。
* Map中存储姓名---归属地
*/
Map<String, String> map = new HashMap<String, String>();
map.put("xiaoqiang", "beijing");
map.put("wangcai", "funiushan");
map.put("daniu", "heifengzhai");
map.put("erhu", "wohudong");
map.put("zhizunbao", "funiushan");
//演示entrySet()。Map.Entry:其实就是一个Map接口中的内部接口。
Set<Map.Entry<String, String>> entrySet = map.entrySet();
for (Iterator<Map.Entry<String, String>> it = entrySet.iterator(); it.hasNext();) {
Map.Entry<String, String> me = it.next();
String key = me.getKey();
String value = me.getValue();
System.out.println(key + ":::" + value);
}
}
}
对于Map.Entry而言,其实Entry也是一个接口,它是Map接口中的一个内部接口。源码我们可以理解为:
interface Map {
//entry就是Map接口中的内部接口
public static interface Entry {
public abstract Object getKey();
public abstract Object getValue();
}
}
class MyDemo implements Map.Entry {
@Override
public Object getKey() {
return null;
}
@Override
public Object getValue() {
return null;
}
}
Map集合的取出所有元素的第三种方式——values()方法
package cn.liayun.map;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class MapDemo2 {
public static void main(String[] args) {
/*
* 取出Map中所有的元素。
* Map中存储姓名---归属地
*/
Map<String, String> map = new HashMap<String, String>();
map.put("xiaoqiang", "beijing");
map.put("wangcai", "funiushan");
map.put("daniu", "heifengzhai");
map.put("erhu", "wohudong");
map.put("zhizunbao", "funiushan");
//演示values()方法,获取所有的值
Collection<String> values = map.values();
for (Iterator<String> it = values.iterator(); it.hasNext();) {
String value = it.next();
System.out.println(value);
}
}
}
练习
练习一
每一个雇员都有对应的归属地。雇员Employee,地址String。雇员属性:姓名,年龄。将雇员和归属地存储到HashMap集合中并取出,注意:姓名和年龄相同的视为同一个雇员,须保证雇员的唯一性。
首先描述雇员,由于要将雇员对象作为键存入HashMap集合中,所以为了保证键的唯一性,雇员类中应该覆盖掉hashCode()和equals()方法。
package cn.liayun.domain;
public class Employee {
private String name;
private int age;
public Employee() {
super();
}
public Employee(String name, int age) {
super();
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 "Employee [name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
然后定义Map容器,将雇员作为键,地址作为值存入,在这里我们只使用keySet()方法进行获取Map集合中的元素。
package cn.liayun.map;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import cn.liayun.domain.Employee;
public class HashMapTest {
public static void main(String[] args) {
Map<Employee, String> map = new HashMap<Employee, String>();//如果改成LinkedHashMap,可以实现有序。
map.put(new Employee("xiaozhang", 24), "北京");
map.put(new Employee("laoli", 34), "上海");
map.put(new Employee("mingming", 26), "南京");
map.put(new Employee("xili", 30), "广州");
map.put(new Employee("laoli", 34), "铁岭");
Set<Employee> keySet = map.keySet();
for (Employee employee : keySet) {
String value = map.get(employee);
System.out.println(employee.getName() + ":" + employee.getAge() + "...." + value);
}
}
}
拓展一
现在我们又有了这样一个需求:按照雇员的年龄进行升序排序并取出。
分析:因为数据是以键值对的形式存在的,所以要使用可以排序的Map集合——TreeMap。
首先描述雇员,为了能按照雇员的年龄进行升序排序,我们可以让自定义的Employee类实现Comparable接口(强制让Employee类具备比较性),覆盖compareTo()方法。
package cn.liayun.domain;
public class Employee implements Comparable<Employee> {
private String name;
private int age;
public Employee() {
super();
}
public Employee(String name, int age) {
super();
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 "Employee [name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public int compareTo(Employee o) {
int temp = this.age - o.age;
return temp == 0 ? this.name.compareTo(o.name) : temp;
}
}
然后定义Map容器,将雇员作为键,地址作为值存入,在这里我们只使用entrySet()方法进行获取Map集合中的元素。
package cn.liayun.map;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import cn.liayun.domain.Employee;
public class TreeMapTest {
public static void main(String[] args) {
Map<Employee, String> map = new TreeMap<Employee, String>();
map.put(new Employee("xiaozhang", 24), "北京");
map.put(new Employee("laoli", 34), "上海");
map.put(new Employee("mingming", 26), "南京");
map.put(new Employee("xili", 30), "广州");
map.put(new Employee("laoli", 34), "铁岭");
//entrySet()
Set<Map.Entry<Employee, String>> entrySet = map.entrySet();
for (Map.Entry<Employee, String> me : entrySet) {
Employee key = me.getKey();
String value = me.getValue();
System.out.println(key.getName() + "::" + key.getAge() + "....." + value);
}
}
}
拓展二
现在我们又有了这样一个需求:按照雇员的姓名进行升序排序并取出。
分析:因为数据是以键值对的形式存在的,所以要使用可以排序的Map集合——TreeMap
由于雇员类我们在上面已描述过,所以在此省略,然后定义一个按姓名排序的比较器,最后定义Map容器,将雇员作为键,地址作为值存入,在这里我们只使用entrySet()方法进行获取Map集合中的元素。
package cn.liayun.map;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import cn.liayun.domain.Employee;
public class TreeMapTest {
public static void main(String[] args) {
//自定义一个比较器
Comparator<Employee> comparator = new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
int temp = o1.getName().compareTo(o2.getName());
return temp == 0 ? o1.getAge() - o2.getAge() : temp;
}
};
Map<Employee, String> map = new TreeMap<Employee, String>(comparator);
map.put(new Employee("xiaozhang", 24), "北京");
map.put(new Employee("laoli", 34), "上海");
map.put(new Employee("mingming", 26), "南京");
map.put(new Employee("xili", 30), "广州");
map.put(new Employee("laoli", 34), "铁岭");
//entrySet()
Set<Map.Entry<Employee, String>> entrySet = map.entrySet();
for (Map.Entry<Employee, String> me : entrySet) {
Employee key = me.getKey();
String value = me.getValue();
System.out.println(key.getName() + "::" + key.getAge() + "....." + value);
}
}
}
练习二
获取字符串(比如”bwaerbctyxbacecrtdcvr”)中每一个字母出现的次数,要求结果格式为:a(2)b(1)d(3)…
分析:通过结果发现,每一个字母都有对应的次数,说明字母和次数之间具有映射关系。注意:当发现有映射关系时,可以选择Map集合,因为Map集合中存放的就是映射关系。本题思路如下:
- 将字符串转换为字符数组。因为要对每一个字母进行操作;
- 定义一个Map集合,因为打印结果的字母有顺序,所以使用TreeMap集合;
- 遍历字符数组,将每一个字母作为键去查Map集合。如果返回null,将该字母和1存入到Map集合中。如果返回不是null,说明该字母在Map集合中已经存在,并有对应次数。那么就获取该次数并进行自增,然后将该字母和自增后的次数存入到Map集合中,覆盖掉原来键所对应的值;
- 将Map集合中的数据变成指定的字符串返回。
package cn.liayun.test;
import java.util.Map;
import java.util.TreeMap;
public class Test {
public static void main(String[] args) {
/*
* 作业:"bwaerbctyxbacecrtdcvr"
* 获取字符串中每一个字母出现的次数,要求结果格式:a(2)b(1)d(3)......
* 思路:
* 1,获取到字母。
* 2,如何获取到字母的次数?
* 发现字母和次数有对应关系,而且对应关系的一方具备唯一性。
* 就想到了Map集合。Map集合就是一张表。
* 3,使用查表法就可以了。
* 先查第一个字母在表中出现的次数,如果次数不存在,说明是第一次出现,将该字母和1存储到表中。
* 依此类推,当要查的次数存在了,将次数取出并自增后,再和对应的字母存储到表中,map表的特点是相同键,值覆盖,哦耶!
* 4,查完每一个字母后,表中存储的就是每一个字母出现的次数。
*/
String str = "b?wa+erbcty=xbace-crtdcvr";
String char_count = getCharCount(str);
System.out.println(char_count);
}
public static String getCharCount(String str) {
//1,将字符串转成字符数组
char[] chs = str.toCharArray();
//2,定义map集合表
Map<Character, Integer> map = new TreeMap<Character, Integer>();
//3,遍历字符数组,获取每一个字母
for (int i = 0; i < chs.length; i++) {
//只对字母操作
if (!(chs[i] >= 'a' && chs[i] <= 'z' || chs[i] >= 'A' && chs[i] <= 'Z')) {
continue;
}
//将遍历到的字母作为键去查表,获取值。
Integer value = map.get(chs[i]);
int count = 0;//用于记录次数。
//如果次数存在,就用count记录该次数;如果次数不存在,就不记录,只对count自增变成1。
if (value != null) {
count = value;
}
count++;
map.put(chs[i], count);
/*
if (value == null) {
map.put(chs[i], 1);
} else {
value++;
map.put(chs[i], value);
}
*/
}
// System.out.println(map);
return toString(map);
}
/*
* 将Map集合中的元素转成指定格式的字符串。a(2)b(1)d(3)......
*/
private static String toString(Map<Character, Integer> map) {
//1,数据多,无论类型是什么,最终都要变成字符串,所以可以使用StringBuilder
StringBuilder sb = new StringBuilder();
//2,遍历集合Map
for (Character ch : map.keySet()) {
Integer value = map.get(ch);
// sb.append(ch).append("(").append(value).append(")");
sb.append(ch + "(" + value + ")");
}
return sb.toString();
}
}
总结
什么时候使用Map集合呢? 当需求中出现映射关系时,应该最先想到Map集合。