黑马程序员-JAVA基础-Java 集合之Map 接口
Map 用于保存具有映射关系的数据,因此Map 集合里保存者两组值,一组值用于保存Map 里的Key ,另一组值用于保存Map 里的Value ,其中key 和 value 都是可以是任何引用类型的数据。
注意:Map 的key 不允许重复,且通过指定的key,总能找到唯一的、确定的value。即key 和 value 之间存在单向一对一关系。
Map 接口的经常用到的实现类:HashMap、Hashtable和TreeMap。
一.Map 集合中的共性方法
1、添加:
> V put(K key, V value) : 添加一个key-value 对,如果当前Map中已有一个与该key 相等的key-value 对,则新的key-value 对会覆盖原来的,并返回原来的value。
> void putAll(Map<? extends K,? extends V> m) 从指定映射中将所有映射关系复制到此映射中。
2、删除:
> void clear() : 从此映射中移除所有映射关系 。
> V remove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除 , 并返回 value 。
3、判断:
> boolean containsKey(Object key) :如果Map 中包含指定key ,则返回 true。
> boolean containsValue(Object value) : 如果Map 中包含一个或多个value , 则返回true。
> boolean isEmpty() : 如果Map 为空,则返回true 。
4、获取:
> V get(Object key) 返回key 对应的value , 如果没有则返回null 。
> int size() 返回Map 中 key-value 对的个数。
> Collection values() : 返回该Map 里所有value 组成的Collection 。
1 public class MapDemo { 2 public static void main(String[] args) 3 { 4 // 定义个 HashMap 集合, key 和 value的类型都为String。 5 Map<String, String> map = new HashMap<String, String>() ; 6 // 添加元素 7 map.put("诛仙", "萧鼎") ; 8 map.put("笑傲江湖", "金庸") ; 9 map.put("神雕侠侣", "金庸") ; 10 map.put("陆小凤传奇", "古龙") ; 11 System.out.println(map); 12 System.out.println(map.put("笑傲江湖", "金——庸")); 13 System.out.println(map); 14 // 删除元素 15 System.out.println(map.remove("笑傲江湖") ); 16 System.out.println(map); 17 // 获取元素 18 System.out.println(map.get("诛仙")); 19 // 获取元素个数 20 System.out.println(map.size()); 21 // 判断 22 System.out.println(map.containsKey("诛仙")); 23 } 24 }
二.HashMap 和 Hashtable 实现类:
HashMap 和 Hashtable 它们之间的关系完全类似于 ArrayList 和 Vector 的关系。它们两个底层都是哈希表数据结构,两者的区别:
1、Hashtable 不可以存入null 键和null 值;但是HashMap 可以存入null 键和null 值。
2、hashtable 是线程安全Map 实现;但HashMap 是线程不安全的实现类。所以HashMap 性能比较高点。
1 public class HashMapDemo { 2 public static void main(String[] args) 3 { 4 Map<String,String> hash = new HashMap<String, String>() ; 5 6 // 给HashMap 集合存入null键或者null值 7 hash.put("111", null) ; 8 hash.put(null, "1112") ; 9 // 取出的结果为 1112 10 System.out.println(hash.get(null)); 11 Map<String,String> table = new Hashtable<String, String>() ; 12 // 下面的代码会出现运行异常:NullPointerException 13 table.put("111", null) ; 14 table.put(null, "1112") ; 15 System.out.println(table.get(null)); 16 } 17 }
类似HashSet , HashMap 和 Hashtable 也不能保证其中的key-value 对的顺序。它们判断两个key的标准也是:
1、先判断两个key 的hashCode 值是不是相等,
2、如果相等则再调用equals方法的返回值来进行判断。返回true 表示两个key值一样;返回false 表示不一样。
1 class Writer 2 { 3 private String name ; 4 public Writer(String name) 5 { 6 this.name = name ; 7 } 8 // get方法 9 public String getName() 10 { 11 return name ; 12 } 13 // 重写hashCode方法 14 public int hashCode() 15 { 16 System.out.println(this.name+"---------hashCode--------"); 17 return this.name.hashCode() ; 18 } 19 // 重写equals方法 20 public boolean equals(Object o) 21 { 22 if (!(o instanceof Writer)) 23 throw new RuntimeException() ; 24 Writer w = (Writer) o ; 25 System.out.println(name+"---------equals--------"+w.name); 26 return this.name.equals(w.getName()) ; 27 } 28 } 29 public class MapDemo1 { 30 public static void main(String[] args) 31 { 32 // 定义个 HashMap 集合, key 的存储的数据类型为Writer, value的类型为String。 33 Map<Writer,String> writerMap = new HashMap<Writer,String>() ; 34 35 // 添加元素: 36 writerMap.put(new Writer("金庸"), "笑傲江湖") ; 37 writerMap.put(new Writer("萧鼎"), "诛仙") ; 38 writerMap.put(new Writer("金庸"), "笑傲江湖") ; 39 40 System.out.println(writerMap); 41 } 42 }
三.TreeMap 实现类
TreeMap 实现的的底层是二叉树的数据结构。线程是不同步的,其特点是:TreeMap 会对集合中所有 key 进行排序;也有两种排序方式:
1、自然排序: TreeMap 的所有key 必须实现Comparable 接口,而且所有key 必须是同一类对象。
2、制定排序: 编写比较器。在创建集合容器时将比较器传入至集合构造器。
1 //创建Books类并且 实现Comparable 接口 2 //如果Books的名字和编号都相同则表示同一本书 3 class Books implements Comparable<Books> 4 { 5 private String name ; 6 private String id ; 7 public Books(String name , String id ) 8 { 9 this.name = name ; 10 this.id = id ; 11 } 12 // get方法 13 public String getName() 14 { 15 return name ; 16 } 17 public String getId() 18 { 19 return id ; 20 } 21 22 // 覆盖compareTo 方法 23 public int compareTo(Books b) 24 { 25 int num = this.id.compareTo(b.getId()) ; 26 if (num == 0) 27 { 28 return this.name.compareTo(b.getName()) ; 29 } 30 return num ; 31 } 32 // 重写hashCode 方法 33 public int hashCode() 34 { 35 return this.name.hashCode()+ this.id.hashCode() ; 36 } 37 // 重写equals 方法 38 public boolean equals(Object o) 39 { 40 if (!(o instanceof Books)) 41 throw new RuntimeException() ; 42 Books b = (Books) o ; 43 return this.name.equals(b.getName())&& this.id.equals(b.getId()) ; 44 } 45 } 46 public class TreeMapDemo { 47 public static void main(String[] args) 48 { 49 Map<Books,String> bookMap = new TreeMap<Books, String> () ; 50 51 bookMap.put(new Books("笑傲江湖","000001"), "金庸") ; 52 bookMap.put(new Books("笑傲江湖","000002"), "金庸") ; 53 bookMap.put(new Books("笑傲江湖","000004"), "金庸") ; 54 bookMap.put(new Books("笑傲江湖","000003"), "金庸") ; 55 bookMap.put(new Books("神雕侠侣","000001"), "金庸") ; 56 57 // 取出方式:entry() 58 Set<Entry<Books, String>> bookEntry = bookMap.entrySet() ; 59 Iterator<Map.Entry<Books, String>> itEntry = bookEntry.iterator() ; 60 while(itEntry.hasNext()) 61 { 62 Map.Entry<Books, String> entry = itEntry.next() ; 63 System.out.println("name:" + entry.getKey().getName() + 64 " id:" + entry.getKey().getId() + 65 " 作者: " + entry.getValue()); 66 } 67 } 68 }
四.Map 集合的两种取出方式:
因为 Map集合中没有Iterator 迭代器,所以如果想将Map 集合中的元素遍历以便就要能用到下面两个方法:
1、Set<Map.Entry<K,V>> entrySet() :返回此映射中包含的映射关系的 Set 视图。映射关系为 Map.Entry<K,V>类型 ;
2、Set<K> keySet():将Map 中所有的键存入到Set集合中 ;
原理是:将Map 集合中的元素取出保存到Set 集合中,再根据Set 集合中的Iterator 迭代器取出。
4.1 、 entrySet()
Map.Entry<K,V> 为一个接口(Map 接口中的内部类),其用法和 Iterator 接口类似。该接口包含如下方法
> boolean equals(Object o) : 比较指定对象与此项的相等性。
> K getKey() : 返回与此项对应的键。
> V getValue() : 返回与此项对应的值。
> int hashCode() : 返回此映射项的哈希码值。
> V setValue(V value) : 用指定的值替换与此项对应的值 。
步骤如下:
1、先获取Map 集合中的key-value 对的关系Set<Map.Entry<K,V>> 集合。
2、获取 Set 集合的迭代器。
3、遍历取出。
1 class Writer 2 { 3 private String name ; 4 public Writer(String name) 5 { 6 this.name = name ; 7 } 8 // get方法 9 public String getName() 10 { 11 return name ; 12 } 13 // 重写hashCode方法 14 public int hashCode() 15 { 16 return this.name.hashCode() ; 17 } 18 // 重写equals方法 19 public boolean equals(Object o) 20 { 21 if (!(o instanceof Writer)) 22 throw new RuntimeException() ; 23 Writer w = (Writer) o ; 24 return this.name.equals(w.getName()) ; 25 } 26 } 27 public class MapDemo1 { 28 public static void main(String[] args) 29 { 30 // 定义个 HashMap 集合, key 的存储的数据类型为Writer, value的类型为String。 31 Map<Writer,String> writerMap = new HashMap<Writer,String>() ; 32 33 // 添加元素: 34 writerMap.put(new Writer("金庸"), "笑傲江湖") ; 35 writerMap.put(new Writer("萧鼎"), "诛仙") ; 36 writerMap.put(new Writer("古龙"), "流星蝴蝶剑") ; 37 38 // System.out.println(writerMap); 39 // 将Map 元素取出存入Set 集合,指定该Set集合只能存入MapEntry<>数据类型。 40 Set<Map.Entry<Writer, String>> setMap = writerMap.entrySet() ; 41 // 建立迭代器 42 Iterator<Map.Entry<Writer,String>> it = setMap.iterator() ; 43 while(it.hasNext()) 44 { 45 // 取出key-value 对 46 Map.Entry<Writer, String> entry = it.next() ; 47 System.out.println("key:"+ entry.getKey().getName()+ 48 "value:"+ entry.getValue()); 49 } 50 } 51 }
4.2 keySet() :
步骤:
1、先获取Map集合的所有键的Set集合。
2、获取Set集合的迭代器。
3、遍历。通过Mao集合get方法获取其对应的值。
1 class Writer 2 { 3 private String name ; 4 public Writer(String name) 5 { 6 this.name = name ; 7 } 8 // get方法 9 public String getName() 10 { 11 return name ; 12 } 13 // 重写hashCode方法 14 public int hashCode() 15 { 16 return this.name.hashCode() ; 17 } 18 // 重写equals方法 19 public boolean equals(Object o) 20 { 21 if (!(o instanceof Writer)) 22 throw new RuntimeException() ; 23 Writer w = (Writer) o ; 24 return this.name.equals(w.getName()) ; 25 } 26 } 27 public class MapDemo1 { 28 public static void main(String[] args) 29 { 30 // 定义个 HashMap 集合, key 的存储的数据类型为Writer, value的类型为String。 31 Map<Writer,String> writerMap = new HashMap<Writer,String>() ; 32 33 // 添加元素: 34 writerMap.put(new Writer("金庸"), "笑傲江湖") ; 35 writerMap.put(new Writer("萧鼎"), "诛仙") ; 36 writerMap.put(new Writer("古龙"), "流星蝴蝶剑") ; 37 // 取出key ,存入Set 集合 38 Set<Writer> setMap = writerMap.keySet() ; 39 // 获取Set集合迭代器 40 Iterator<Writer> it = setMap.iterator() ; 41 while(it.hasNext()) 42 { 43 Writer w = it.next() ; 44 System.out.println("key:"+w.getName() + 45 "value:" + writerMap.get(w)); 46 } 47 48 } 49 }
五.Map 集合练习:
1、要求:
每个学生都有一个归属地。
学生Student , 地址 String
学生的属性:姓名、年龄
注意:姓名和年龄相同的视为同一个学生。
1 class Student 2 { 3 private String name ; 4 private int age ; 5 public Student(String name , int age) 6 { 7 this.name = name ; 8 this.age = age ; 9 } 10 // get方法: 11 public String getName() 12 { 13 return name ; 14 } 15 public int getAge() 16 { 17 return age ; 18 } 19 // instanceof 判断函数: 20 private Student myIstanceof(Object o) 21 { 22 if (!(o instanceof Student)) 23 throw new RuntimeException() ; 24 return (Student) o ; 25 } 26 // 重写equals 方法 27 public boolean equals(Object o) 28 { 29 Student stu = myIstanceof(o); 30 return this.name.equals(stu.getName()) && this.age == stu.getAge() ; 31 } 32 // 重写hashCode方法 33 public int hashCode() 34 { 35 return name.hashCode()+age*39 ; 36 } 37 } 38 public class Demo { 39 public static void main(String[] args) 40 { 41 // 创建Map 类: 42 Map<Student,String> stuMap = new HashMap<Student, String>() ; 43 44 stuMap.put(new Student("lisi002" , 11), "北京" ) ; 45 stuMap.put(new Student("lisi001" , 14), "上海" ) ; 46 stuMap.put(new Student("lisi005" , 10), "天津" ) ; 47 stuMap.put(new Student("lisi005" , 19), "天津" ) ; 48 stuMap.put(new Student("lisi001" , 19), "长沙" ) ; 49 50 stuMap.put(new Student("lisi001" , 14), "北京" ) ; 51 52 // 第一种取出方式:keySet() 53 Set<Student> stuSet = stuMap.keySet() ; 54 Iterator<Student> it = stuSet.iterator() ; 55 while(it.hasNext()) 56 { 57 Student stu = it.next() ; 58 System.out.println("name:"+stu.getName() 59 +" age:"+stu.getAge() + " 地址:" + stuMap.get(stu)); 60 } 61 System.out.println("------我是分割线------"); 62 // 第二种取出方式:entry() 63 Set<Map.Entry<Student, String>> stuEntry = stuMap.entrySet() ; 64 Iterator<Map.Entry<Student, String>> itEntry = stuEntry.iterator() ; 65 while(itEntry.hasNext()) 66 { 67 Map.Entry<Student, String> entry = itEntry.next() ; 68 System.out.println("name:" + entry.getKey().getName() + 69 " age:" + entry.getKey().getAge() + 70 " 地址: " + entry.getValue()); 71 } 72 } 73 }
2、字母出现的次数:
输入一段字符串,获取该字符串中的字母出现的次数。
希望打印结果:
字母1(次数)字母2(次数)....
通过结果发现,每一个字母都有对应的次数,说明字母和次数间有对应关系。
1 public class TreeMapTest { 2 public static void main(String[] args) 3 { 4 Scanner scan = new Scanner(System.in) ; 5 String str = scan.nextLine() ; 6 Map<Character,Integer> CNTree = new TreeMap<Character, Integer>() ; 7 // 存入Map集合。 8 char[] chs = str.toCharArray() ; 9 for (int i = 0 ; i < chs.length ; i ++) 10 { 11 Integer values = CNTree.get(chs[i]) ; 12 13 if (values == null ) 14 CNTree.put(chs[i], 1) ; 15 else 16 CNTree.put(chs[i], values+1) ; 17 } 18 19 // 遍历Map集合 20 Set<Character> charSet = CNTree.keySet() ; 21 Iterator<Character> it = charSet.iterator() ; 22 while(it.hasNext()) 23 { 24 Character character = it.next() ; 25 System.out.println(character.charValue() +" "+ CNTree.get(character)); 26 } 27 } 28 }
思路:
1、将字符串转换成字符数组(因为要对每一个字母进行操作)。
2、定义一个map集合,使用TreeMap 集合。
3、遍历字符数组:
a、将每一个字母作为键去查Map集合。
b、如果返回null,将该字母和1存入Map集合中。
c、如果返回不是null,则自增后,存入Map集合中。
4、将Map集合中的数据打印。