这一篇我们说说集合框架的另一个家族Map接口。它是和Colletion接口是并列为集合框架的始祖啊。
一、Map接口
Map<K,V>,一次添加一对元素(又称键值对)。K是键,V是值。而Collection一次添加一个元素。Map集合也称为双列集合,Collection集合也称为单列集合。其实Map集合中存储的就是键值对,且保证键(K)的唯一性。
Map常用的子类:
1、Hashtable:内部结构是哈希表,是同步的。不支持null作为键和值。
2、HashMap:内部结构是哈希表,不是同步的,支持null作为键和值。
3、TreeMap:内部结构是二叉树,不是同步的,支持null作为键和值。
Map接口常用的方法如下:
1、添加
value put(key,value);//返回前一个与key关联的值,如果没有则返回null
2、删除
value remove(key);//根据指定的key删除这个键值对
void clear();//清除这个Map集合
3、判断
boolean containsKey(key);
boolean containsValue(value);
boolean isEmpty();//判断是否为空
4、获取
value get(key);//用过键来返回值,如果没值该键返回null。当然可以通过返回null来判断是否包含指定的键(K)
int size();//获取键值对的个数
二、Map接口是如何实例化的,如何遍历元素的??
这里演示一个HashMap实例化的例子:
Map<Integer,String> map=new HashMap<Integer,String>();//实例化一个HashMap对象
注意:问题:如何获取map中的所有的元素???
取出map中的元素,原理如下:通过keySet方法获取map中所有键所在的Set集合中,再通过Set的迭代器获取到每个键。然后对每个键通过map集合的get()方法获取其所对应的值。
代码演示如下:
package com.wq.map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* 测试Map的一些基本的方法
*
* @author LULEI
*
*/
public class mapDemo1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Map<Integer, String> hm=new HashMap<Integer,String>();//实例化对象
testMapDemo1(hm);//这个方法实现了一些基本方法
System.out.println();
System.out.println();
testMapDemo2(hm); //这个方法实现Map集合中如何取出所有的元素
//1、要使用到keySet()方法来获取key值得Set集合
//2、依次的迭代Set集合中的key值,最后通过get(key)来获取键值对
}
public static void testMapDemo2(Map<Integer, String> hm) {
// TODO Auto-generated method stub
String str2=hm.put(2, "lisi");
//System.out.println("此时给相同的key赋键值,返回的上一个键值是:"+str2);//此时lisi //会覆盖wangcai的,同时put方法会返回之前的键值对
hm.put(8, "wq");
hm.put(7, "ll");
hm.put(6, "pp");
Set<Integer> s=hm.keySet();//获取key的Set集合
//使用迭代器来迭代集合
for(Iterator<Integer> it=s.iterator(); it.hasNext();){
Integer i=it.next();
System.out.println("使用迭代获取的元素:"+hm.get(i) );
}
}
public static void testMapDemo1(Map<Integer, String> hm) {
//添加
String str1=hm.put(2, "wangcai");
System.out.println("此时给相同的key赋键值,返回的上一个键值是:"+str1);//此时由于key刚刚被赋值,所以之前是没有键值对的
String str2=hm.put(2, "lisi");
System.out.println("此时给相同的key赋键值,返回的上一个键值是:"+str2);//此时lisi会覆盖wangcai的,同时put方法会返回之前的键值对
hm.put(7, "ll");
hm.put(8, "wq");
hm.put(6, "pp");
System.out.println("所有的键值对如下:"+hm);
//获取
System.out.println("获取相应的key值得键值:"+hm.get(7));
System.out.println("获取相应的key值得键值:"+hm.get(4));
//删除
hm.remove(6);//删除key为6的键值
System.out.println("删除key为6的键值后的map"+hm);
}
}
运行结果如下:
此时给相同的key赋键值,返回的上一个键值是:null
此时给相同的key赋键值,返回的上一个键值是:wangcai
所有的键值对如下:{2=lisi,6=pp, 7=ll, 8=wq}
获取相应的key值得键值:ll
获取相应的key值得键值:null
删除key为6的键值后的map{2=lisi, 7=ll, 8=wq}
使用迭代获取的元素:lisi
使用迭代获取的元素:pp
使用迭代获取的元素:ll
使用迭代获取的元素:wq
上面提到的只是一种方法,另一种方法是entrySet()。该方法将键与值得映射关系作为对象存储到了Set集合中,而这个映射关系的类型就是Map.EntryKey类型。(打个比方:键是丈夫,值就是妻子,映射关系就是结婚证)。Entry本身就是一个嵌套的静态接口。
图解如下:
代码演示如下:
//Person类
package com.wq.person;
public class Person extends Object implements Comparable{
private String name;
private int age;
public Person() {
super();
// TODO Auto-generated constructor stub
}
public Person(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 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 (!(obj instanceof Person))
return false;
Person other = (Person) 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 String toString() {
// TODO Auto-generated method stub
return name+"...."+age;
}
//需要覆写的compareTo方法
//这里我的思路是先比较age的大小,如果age相等,则
//接着比较name的字典顺序大小
@Override
public int compareTo(Object o) {
// TODO Auto-generated method stub
Person per=(Person) o;//实现强制类型转换
if(this.age>per.age){
return 1;
}else if(this.age<per.age){
return -1;
}else{//如果年纪相等就比较名字的字典顺序大小
int temp=this.name.compareTo(per.name);
return temp;
}
}
}
//student类继承Person类
package com.wq.person;
public class Student extends Person{
private String name;
private int age;
public Student(String name, int age) {
super(name,age);
}
public Student(){
super();
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
//测试HashMap类
package com.wq.map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import com.wq.person.Student;
public class Hashmaptest {
public static void main(String[] args) {
// TODO Auto-generated method stub
HashMap<Student,String> hm=new HashMap<Student,String>();
//添加学生对象,和学生的归属地
hm.put(new Student("lisi",24 ),"南京");
hm.put(new Student("zhangsan",44 ),"北京");
hm.put(new Student("wangwu",32 ),"天津");
hm.put(new Student("xiaoqiang",18 ),"上海");
hm.put(new Student("xiaoqiang",18 ),"后海");
Set<Student> s=hm.keySet();
Iterator<Student> it=s.iterator();
while(it.hasNext()){
Student st=it.next();
String value=hm.get(st);
System.out.println(st.getName()+".."+st.getAge()+"...."+value);
}
}
}
运行代码如下:
xiaoqiang..18....后海
wangwu..32....天津
zhangsan..44....北京
lisi..24....南京
//测试TreeMap类
//自定义比较器
package com.wq.comparator;
import java.util.Comparator;
import com.wq.person.Person;
public class ComparatorByName implements Comparator {
/**
* 覆写compare方法,生成一个比较器。这里我们用name的字典顺序来作为比较
*/
@Override
public int compare(Object o1, Object o2) {
// TODO Auto-generated method stub
Person p1=(Person)o1;
Person p2=(Person)o2;
int temp=p1.getName().compareTo(p2.getName());//获取两个对象的name比较的结果
return temp==0?p1.getAge()-p2.getAge():temp;
}
}
//TreeMap测试部分
package com.wq.map;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import com.wq.comparator.ComparatorByName;
import com.wq.person.Student;
public class TreeMaptest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//添加一个比较器,用来排序
TreeMap<Student,String> hm=new TreeMap<Student,String>(new ComparatorByName());
//添加学生对象,和学生的归属地
hm.put(new Student("lisi",24 ),"南京");
hm.put(new Student("zhangsan",44 ),"北京");
hm.put(new Student("wangwu",32 ),"天津");
hm.put(new Student("xiaoqiang",18 ),"上海");
hm.put(new Student("xiaoqiang",28 ),"后海");
//使用entrySet来获取全部元素
Iterator<Map.Entry<Student, String>> it=hm.entrySet().iterator();
while(it.hasNext()){
Map.Entry<Student, String> mp=it.next();
Student st=mp.getKey();
String value=mp.getValue();
System.out.println(st.getName()+".."+st.getAge()+"...."+value);
}
}
}
运行结果:
lisi..24....南京
wangwu..32....天津
xiaoqiang..18....上海
xiaoqiang..28....后海
zhangsan..44....北京
相信以上的代码能够帮助我们更好地理解Map的子类。以及获取所有子类元素的方法:keySet()和EntrySet()两种方法。接下来看看练习:
练习题:” aFGDHRTYdshgfd./,/g1uAAD898 styefbhbhad”,这样一个随机的字符串。获取该字符串中每个字符出现的次数。并最终以a(n),b(m)……这样的格式打印出来。
分析如下:对于结构的显示,字符和出现次数之间存在着映射的关系。而且这种关系很多。既然很多我们就需要存储,但是能存储映射关系的有数组和Map集合。这时我们需要考虑:关系一方有序号编码吗?答案是没有,于是我们就选用Map集合,同时又发现可以保证唯一性的一方具备着顺序如:a,b,c,d,……。所以最后的选择是TreeMap集合。
操作思路:这个集合最后存储的是字符与次数的对应关系。
1、因为操作的是字符串中的字符,所以先将字符串变成字符数组。
2、遍历字符数组,用每一个字符作为键去查询Map集合这个表。
3、如果该键不存在,就将该字母作为键,1作为值,存储到Map集合中。
4、如果该键存在,就将该字符键对应的值加1,在将该字母与加1后的值存在Map集合中。键相同,只会覆盖,这样就记录了字符的次数。
5、遍历字符数组结束后,Map集合就记录了所有的字符出现的次数。一次迭代输出即可是实现。
代码实现如下:
package com.wq.map;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
/**
* 练习:" aFGDHRTYdshgfd./,/g1uAAD898 styefbhbhad "获取该字符串中,
* 每一个字母出现的次数,要求打印的结果如下
* a(1)b(2)c(..)...
* @author LULEI
*
*/
public class MapPrictice {
public static void main(String[] args) {
// TODO Auto-generated method stub
//声明要变换的字符串
String str="aFGDHRTYdshgfd./,/g1uAAD898 styefbhbhad";
str=StringTocharNum(str);
System.out.println(str);
}
public static String StringTocharNum(String str) {
// TODO Auto-generated method stub
//1、第一步是将字符串转换成字符数组
char[] ch=str.toCharArray();
//2、定义一个map集合,用于存储字母(键)和出现的次数(值)
Map<Character,Integer> mp=new TreeMap<Character,Integer>();
//3、开始进行循环的判断
//当出现非字母的字符时,增加如下的判断
for (int i = 0; i < ch.length; i++) {
if(!(ch[i]>='a' && ch[i]<='z'|| ch[i]>='A' && ch[i]<='Z')){
continue;//如果不是字符则结束本次循环
}
Integer value=mp.get(ch[i]);//注意这里返回的是int的包装类。之前Map是这样定义的Map<Character,Integer>
if(value==null){
mp.put(ch[i], 1);
}else{
mp.put(ch[i], value+1);
}
}
//4、将字母以次数打印出来
return printCharNum(mp);//调用打印函数
}
private static String printCharNum(Map<Character, Integer> mp) {
//定义一个可以改变字符长度的字符串
StringBuilder sb=new StringBuilder();
//取出Map中的所有元素
Iterator<Character> it=mp.keySet().iterator();
while(it.hasNext()){
Character ch=it.next();
Integer in=mp.get(ch);
sb.append(ch+"("+in+")");//直接在后面添加字符串
}
return sb.toString();//最后转换为string类型输出
}
}
运行结果如下:
A(2)D(2)F(1)G(1)H(1)R(1)T(1)Y(1)a(2)b(2)d(3)e(1)f(2)g(2)h(3)s(2)t(1)u(1)y(1)
XXiaoLEI整理于2015-04-06 。