黑马程序员 java基础之HashMap和TreeMap

Map集合概述

  

/* Map<K,V>: K (key)- 此映射所维护的键的类型 V (Value)- 映射值的类型 Map集合:该集合存储键值对.一对一对往里存.而且要保证键的唯一性 */ package map; import java.util.*; class MapDemo { public static void main(String[] args) { //测试LinkedHashMap->存入顺序和取出顺序一致 //此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序) LinkedHashMap<String,String>lm = new LinkedHashMap<String,String>(); lm.put("1","abc"); lm.put("2","bcd"); lm.put("3","abc"); lm.put("4","abcd"); lm.put("5","abc"); System.out.println(lm); lm.put("5","defg"); System.out.println(lm); } } /* 重要的实现类: Hashtables:底层是哈希表数据结构,不可以存入null键null值.HashTable是线程同步的.效率低 JDK 1.0 为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。 HashMap:底层是哈希表数据结构,可以存入null键null值.HashMap是线程非同步的.效率高 JDK1.2 依然需要实现hashCode和equals TreeMap:①底层是红黑树(自平衡二叉查找树)数据结构.线程不同步. ②可以用于给map集合中的键(Key)进行排序和Set很像. 其实,set底层就是使用了Map集合 HashSet 底层用到了 HashMap 很好解释了为什么要复写hashCode和equals TreeSet 底层用到了 TreeMap 而TreeMap运用的就是红黑树 */

  Map常用操作:

/*
一些常用操作:
.添加
V put(K key, V value)
将指定的值与此映射中的指定键关联(可选操作)。
void putAll(Map<? extends K,? extends V> m)
从指定映射中 将所有映射关系 复制到此映射中(可选操作)。
.删除
void clear()
V remove(Object key)
如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
.判断
boolean containsKey(Object key)
如果此映射包含 指定键的映射关系,则返回true.
boolean containsValue(Object Value)
如果此映射将 一个或多个键映射到指定值 ,则返回true.
.获取
V get(Object key)
返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
int size()
返回此映射中的 键-值映射 关系数。
Collection<V> values()
返回此映射中包含的值(Value)的 Collection 视图。
※Set<Map.Entry<K,V>> entrySet()
返回此映射中包含的映射关系的 Set 视图。
※ Set<K> keySet()
返回此映射中包含的键的 Set 视图。
*/
package map;
import java.util.*;
class MapDemo2{
public static void print(Object obj){
System.out.println(obj);
}
public static void main(String[] args){
Map<String,String> map = new HashMap<String,String>();
//添加元素
print("put "+map.put("01","zhangsan"));//null 原来HashMap中没有01对应的value->返回null
print("put "+map.put("01","heihei"));//zhangsan 返回旧的01对应的heihei,并且把01对应的zhangsan替换掉(保证key唯一性)
/*
也就是说,在添加时,新的key=value与旧的key=value
出现key相同->新value会覆盖旧value,并且返回旧value.
*/
map.put("02","Lisi");
map.put("03","wangwu");
print("containsKey: "+map.containsKey("04"));//false
print("remove04: "+map.remove("04"));//null
print("remove02: "+map.remove("02"));//Lisi
print(map);//{01=zhangsan,03=wangwu}//依然元素转换成字符串形式输出,使用的是AbstractMap中的toString();
//注意打印顺序和存入顺序可能不同->底层使用的为哈希表
//获取
print("get01: "+map.get("01"));//zhangsan
print("get04: "+map.get("04"));//null
map.put(null,"maliu");//虽然可以加入,但没有意义
map.put("04",null);//同上
//可以通过get方法的返回值来判断一个键是否存在,返回是否为null判断
Collection<String> coll = map.values();
print(coll);
print(map);
}
}

  entrySet()与keySet()

/*
怎样将Map中所有value,Key取出?
.Set<K> keySet()
将Map中所有的Key存入到Set集合.
因为set集合具备迭代器.利用迭代器取出Key,
根据Map中的get方法,
获取每一个键对应的值.
Map集合的取出原理:将map集合转成set集合,在通过迭代器取出
(画一个示意图)
.Set<Map.Entry<K,V>> entrySet()
将map结合中的映射关系存入到了set集合中,而这个关系的数据类型就是Map.Entry
(画一个示意图)
Map.Entry:表示Map中的关系类型
而Map.Entry<K,V>:指定关系中Key和Value的操作类型
Set<Map.Entry<K,V>>:泛型嵌套
老毕经典比喻:(霸气侧漏(*^__^*))
假如存入的键值对为丈夫和妻子(当然了一夫一妻制:一个丈夫对应一个妻子)
①keySet:把Map中的丈夫取出来放在Set->通过丈夫找出对应的妻子
②entrySet:把Map中的丈夫和妻子的关系取出来(假设为结婚证)->存放在Set中
->通过结婚证,结婚证上有丈夫和妻子啊(Map.Entry)
->调用结婚证特有方法(getKey,getValue)
->获取到丈夫和妻子
*/
package map;
import java.util.*;
class MapDemo3{
public static void main(String[] args){
Map<String,String> map = new HashMap<String,String>();
map.put("02","zhang");
map.put("03","wang");
map.put("04","xu");
//获取map集合中所有key的Set集合
Set<String> s = map.keySet();//Set<String>里面指明key的泛型,因为获取的是key的集合
for(Iterator<String>it = s.iterator();it.hasNext();)
System.out.println(map.get(it.next()));
//entrySet方法,注意用的HashMap存入和取出的顺序不一定一致
Set<Map.Entry<String,String>> s2= map.entrySet();
for(Iterator<Map.Entry<String,String>> it2 = s2.iterator();it2.hasNext(); ){
Map.Entry<String,String> me=it2.next();
System.out.println(me.getKey()+"..."+me.getValue());
}
}
}
/*
分析Map.Entry:
其实Entry也是一个接口,它是Map接口中的一个内部接口.
src中:
//接口经过编译后生成.class文件,可以类比下 内部类
public interface Map<K,V>{
public static interface Entry<K,V>{
K getKey();
V getValue();
V setValue(V value);
}
}
为什要把Entry定义在Map内部?
Entry表示Map中Key与Value的关系,其实Key和Value被
封装在了Entry类(HashMap中一个内部类实现了Map中的Entry接口)中.
static class Entry<K,V> implements Map.Entry<K,V>{
final K key;
V value;
Entry<K,V> next;
int hash;
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
public final K getKey() {
return key;
}
public final V getValue() {
return value;
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
.....
}
*/

  keySet-entrySetkeySetentrySet

HashMap集合示例:

/*
每一个学生都有对应的归属地
学生Student,地址String.
学生属性:姓名和年龄相同的视为同一个学生
保证学生的唯一性
*/
//描述学生
//注意这里需要存入到HashMap中
//之前HashSet之所以需要复写hashCode和equals
//因为底层用的其实是HashMap(更确切的说是哈希表)
//为了能够实现对哈希表中的元素进行存储和获取->用作Key的对象需要复写这两个方法
//要想存入到HashMap中,更需要复写
package map;
import java.util.*;
class Student implements Comparable<Student>{//如果Studet继承Person使用Person扩展性更好
private String name;
private int age;
Student(String name,int age){
this.name=name;
this.age=age;
}
public int hashCode(){
return name.hashCode()+age*37;
}
public boolean equals(Object obj){
if(!(obj instanceof Student))
// return false; 返回false没意义,因为不同对象比较没意义
throw new ClassCastException("类型不匹配");//比较好
else
return this.name==((Student)obj).name && this.age==((Student)obj).age;
}
//按姓名比较
public int compareTo(Student stu){
int num=this.name.compareTo(stu.name);
if(num==0)
return this.age-stu.age;
else
return num;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
}
class MapTest{
public static void main(String[] args){
Map<Student,String> map = new HashMap<Student,String>();
map.put(new Student("01",12),"abc");
map.put(new Student("01",12),"bcd");//与第一个key相同,value不同
map.put(new Student("01",12),"abc");//与第一个key,value均相同
map.put(new Student("02",23),"abc");//与第一个key不同,value相同
/*
取出结果:
entrySet: keySet:
-12-abc abc
-23-abc abc
*/
//keySet
//Set<Student> s = map.keySet();
for(Iterator<Student> it = map.keySet().iterator();it.hasNext(); ){
System.out.println(map.get(it.next()));
}
//entrySet
//Set<Map.Entry<Student,String>> s2 = map.entrySet();
for(Iterator<Map.Entry<Student,String>> it = map.entrySet().iterator();it.hasNext(); ){
Map.Entry<Student,String> me=it.next();
Student stu = me.getKey();
System.out.println(stu.getName()+"..."+stu.getAge()+"..."+me.getValue());
}
}
}

  

/*
需求:对学生对象的年龄进行升序排序
因为数据是以键值对形式存在的.
所以要使用可以排序的Map集合-TreeMap
*/
package map;
import java.util.*;
//自定义比较器,按年龄进行比较
class MyComp implements Comparator<Student>{
public int compare(Student stu_1,Student stu_2){
int num=stu_1.getAge()-stu_2.getAge();
if(num==0)
return stu_1.getName().compareTo(stu_2.getName());
return num;
}
}
class MapTest2{
public static void printMap(Map<Student,String> map){
for(Iterator<Map.Entry<Student,String>> it = map.entrySet().iterator();it.hasNext(); ){
// map.entrySet().iterator();//首先获得一个存储关系对象的集合实体
Map.Entry<Student,String> me=it.next(); //调用iterator()获取一个相应的迭代器实体
Student stu = me.getKey();
System.out.println(stu.getName()+"..."+stu.getAge()+"..."+me.getValue());
}
}
public static void main(String[] args){
Map<Student,String> map = new TreeMap<Student,String>(new MyComp());
map.put(new Student("zhang",18),"BJ");
map.put(new Student("liu",12),"SH");
map.put(new Student("wang",20),"NJ");
map.put(new Student("xu",16),"TJ");
//在构造一个新的TreeMap,使用自然顺序排序
Map<Student,String> map2 = new TreeMap<Student,String>(map);
System.out.println(map);//输出map集合,其中按Key从小到大顺序排列
//但这样输出的Key为对象字符串形式.
printMap(map);
System.out.println("\n");
printMap(map2);
}
}

TreeMap练习2:

/*
"sdfgzxcvasdfxcvdf"
获取该字符串中的字母出现次数
希望打印结果:a(1)c(2)......
每一个字母都有对应的次数.
说明字母和次数之间都有映射关系
当发现有映射关系(一个对应一个)时,可以选择map集合
什么时候使用map集合?
像上面的情况那样(数据之间存在映射关系)->首先考虑map集合
思想:
①首先把字符串转化成字符数组
②拿着字符去map集合中遍历->遍历完未找到a(可以令其返回null)->存入a,1(表示出现一次)
->找到转③
③说明a在map中存在对应的key,此时,取出a对应的value->令value自增->重新存入
④其余字符同理
*/
package map;
import java.util.*;
class TreeMapTest{
//方法一:利用containsKey
public static String charCount(String str,Map<Character,Integer> tm){
char[] ch = str.toCharArray();//转换成字符数组,便于操作
for(int i=0;i<ch.length;++i){
Character c=new Character(ch[i]);
if(!tm.containsKey(c))//没有该Key,加入Map集合
tm.put(c,new Integer(1));
else//有,则替换掉该key对应的value
tm.put(c,new Integer(tm.get(c).intValue()+1));
}
StringBuilder sb = new StringBuilder();
//利用entrySet->转换成指定格式的字符串返回
for(Iterator<Map.Entry<Character,Integer>>it=tm.entrySet().iterator();it.hasNext();){
Map.Entry<Character,Integer> me=it.next();
sb.append("("+me.getKey()+")"+me.getValue());
}
return sb.toString();
}
//方法二:利用get
public static String charCount_2(String str,Map<Character,Integer> tm){
char[] ch = str.toCharArray();
for(int i=0,count=0;i<ch.length;++i,count=0){
if((ch[i]<'a'||ch[i]>'z')&&(ch[i]<'A'||ch[i]>'Z'))//非字母字符,不再计数
continue;
Integer value=tm.get(ch[i]);
if(value!=null)
count=value;//count简化了两次调用put
++count;
tm.put(ch[i],count);
/*
if(value==null)//Key没有对应的Value,返回null,加入集合
tm.put(ch[i],1);
else
tm.put(ch[i],value+1);
*/
}
String mapStr="";//存储集合字符串形式
//利用entrySet->转换成指定格式的字符串返回
for(Iterator<Map.Entry<Character,Integer>>it=tm.entrySet().iterator();it.hasNext();){
Map.Entry<Character,Integer> me=it.next();
mapStr += "("+me.getKey()+")"+me.getValue();
}
return mapStr;
}
public static void main(String[] args){
TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>();
String str = "abae+c";
//System.out.println(charCount(str,tm));
//如果再次使用同一个Map,第二种方法,在遍历a时,会把a对应的Value+1
System.out.println(charCount_2(str,tm));
}
}
/*
//TreeMap/HashMap在使用迭代器方法时,使用了集合的增,删方法时,也会报并发修改异常
tm.put('b',1);
while(it.hasNext())
{
tm.put('a',1);
System.out.println(it.next());
}
*/
/*
总结:该题和以前做过的一个去除集合中的重复元素
使用的同一个算法:
①新建一个集合容器
②每次添加看看该容器是否包含添加的元素
③有则不在添加,没有则加入.
*/

  

posted @ 2013-05-05 09:36  xiewen3410  阅读(743)  评论(0编辑  收藏  举报