java程序员从笨鸟到菜鸟之(二十九)集合之Map

来源:互联网 发布:java ant pdf 编辑:程序博客网 时间:2024/06/05 04:13

上一节中我们对Set集合和Map集合的数据结构有了一些简单了解,这节我们来深入了解一些问题

Map集合特点

1:Map集合的数据结构针对键有效,跟无关!!!------重要事情说三遍

2:Map是双列的(是双列集合的根接口)

3:将键映射到值的对象
4:一个映射不能包含重复的键

5:每个键最多只能映射到一个值

首选回顾一下Map集合的一些常用方法

Map集合的功能概述
a:添加功能 
V put(K key,V value):添加元素,这个其实还有另一个功能---替换----//键相同 值覆盖 
如果键是第一次存储,就直接存储元素,返回null
如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值
b:删除功能
void clear():移除所有的键值对元素
V remove(Object key):根据键删除键值对应元素,并把值返回

                  remove()说明 

                  参数:键对象-------返回值:值对象-------方法操作结果是:此键值对被删除

 
c:判断功能
boolean containsKey(Object key):判断集合是否包含指定的键
boolean containsValue(Object value):判断集合是否包含指定的值
boolean isEmpty():判断集合是否为空
d:获取功能---重点掌握
Set<Map.Entry<K,V>> entrySet(): 返回一个键值对Set集合

 V get(Object key):根据键获取值
Set<K> keySet():获取集合中所有键的集合
Collection<V> values():获取集合中所有值的集合

说明返回值类型也能看出来Map集合中键和值对象的特点
e:长度功能
int size():返回集合中的键值对(元素)的个数

实例1

package morning;import java.util.HashMap;import java.util.Map.Entry;import java.util.Set;public class Demo1 {public static void main(String[] args) {        /**         * 注意问题         * (1)hashMap的put方法的有关说明         * 1)返回值null或其他---返回值为null表示集合中没有此键值,添加成功         * 2)如果是其它,表示集合中已经有该键对象,返回被覆盖的值对象         * 3)参数中的键和值均可以为null         *          */HashMap<Integer, String> hashMap = new HashMap<Integer, String>();System.out.println(hashMap.put(1, "张三"));//说明返回值类型String s=hashMap.put(2, "李四");System.out.println(s);hashMap.put(3, "王五");String s1=hashMap.put(3,"麻六");//说明添加相同的键对象时返回值类型System.out.println(s1);System.out.println(hashMap);//底层重写toString了,所以不是地址hashMap.clear();System.out.println(hashMap);//底层字符串的拼接//刪除元素后重新添加元素hashMap.put(1, "习大大");                hashMap.put(2, "彭麻麻");hashMap.put(3, "邓爷爷");String remove = hashMap.remove(3);/** * remove()说明 * 参数:键对象 * 返回值:值对象 * 方法操作结果是:此键值对被删除 */System.out.println("删除键对象对应的值对象:"+remove);System.out.println("删除后的元素"+hashMap);//获取键集合----Set集合特点Set<Integer> keySet = hashMap.keySet();//遍历键集合---由键对象--→得到值对象for(Integer in:keySet){System.out.println(in+"---"+hashMap.get(in));}//hashMap特有的方法得到键值对的集合----用Set存储----重点掌握Set<Entry<Integer, String>> entrySet = hashMap.entrySet();//for循环遍历每一对键值对对象for(Entry<Integer, String> s2:entrySet){//看看底层重写了toString方法了码?---重写了---自定义类型呢System.out.println(s2);}}}
补充:对于entrySet()方法的理解---上图

匿名内部类的形式创建对象,重写了Map中的entrySet()方法,Entry<K,V>是Map<K,V>的匿名内部类

两个重要的方法getKey()和getValue()的方法


问题1:HashMap和TreeMap添加键对象可以为null吗?

分析:最好的办法是验证一下

实例2

package afternoon;import java.util.Comparator;import java.util.HashMap;import java.util.TreeMap;public class Demo1 {public static void main(String[] args) {HashMap<String, String> hashMap = new HashMap<String, String>();hashMap.put(null, null);System.out.println(hashMap);System.out.println("---------------------------------");TreeMap<String, String> treeMap = new TreeMap<String,String>();/** * TreeMap类型键可以为null码? * treeMap.put(null,null);(1) * treeMap.put(null, "1");(2) * (1)、(2)均报错 */treeMap.put("1", null);System.out.println(treeMap);System.out.println("----------------------------------------");}}

分析原因---对于TreeMap的分析首先明确String类是默认实现了Comparable接口的, 因此TreeMap无需我们传入比较器(内部比较器);这里报了空指针的原因,从jdk7源码来看,当向集合中添加数据的时候,调用put方法会去调用String类的compareTo方法, 而这时候,由于传入的键值为null,调用其compareTo方法会发生空指针异常------Java.lang.NullPointerException,这是由于调用comPareTo()方法必须确保两个对象是可比较的,如果值为null,无法比较;jdk8在将其转型之前,直接做了判断如果key=null,则直接抛出空指针异常。
设计角度:因为TreeMap底层数据结构是二叉树,必须对键值进行比较,插入节点位置,而此时如果是null,无法进行比较,该键值(null)放到哪里?显然不合理

实例3 CompareTo()方法

package afternoon;public class Demo1 {public static void main(String[] args) {   String b=null;           String a="3"; a.compareTo(b);}}

注意:compareTo的两个对象必须是可以比较的-----负责会出现-----java.lang.NullPointerException

分析原因--对于HashMap的分析:首先应该明确一点,HashMap底层的数据结构是哈希表,也就是数组加上链表的形式(jdk1.7),jdk1.8中加入了红黑树,那么如果向集合中加入null值,意味着什么?

设计角度:我们知道底层有一个table数组,数组的索引为对应的hash值,键值就是以该hash值为索引的元素,为该条链表对应的头指针,如果为null,就表示该链表的最后一个元素,所以有其实际意义,所以设计的时候没有让其抛出异常

现在问题来了,如果我们想向TreeMap集合存储null值,该怎么办

解决方法:我们可以传入外部比较器,通过匿名内部类的形式创建一个Comparator接口的子实现类,重写compare()方法,以实现强制其存储null键

实例4

package afternoon;import java.util.Comparator;import java.util.TreeMap;public class Demo1 {TreeMap<String, String> treeMap2 = new TreeMap<String,String>(new Comparator<String>() {@Overridepublic int compare(String s1,String s2) {// TODO 自动生成的方法存根if (s1 == null){                return 2;//返回值为正,说明存在左边}            else {                return s2.charAt(0) - s1.charAt(0);            }        }});treeMap2.put(null,"3");System.out.println("管不管用,看疗效"+treeMap2);}}

实例5加深理解Map数据结构只对键有关,与值无关

Student类

package afternoon;public class Student {private String name;private int age;public Student() {super();// TODO Auto-generated constructor stub}public Student(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;}}

测试类

package afternoon;import java.util.Comparator;import java.util.HashMap;import java.util.TreeMap;public class Demo1 {//Student为自定义类型TreeMap<Student, String> map = new TreeMap<Student,String>();//自然必须map.put(new Student("小红",24), "s001");map.put(new Student("小红",24), "s002");System.out.println(map);//(1)TreeMap<String, Student> map1 = new TreeMap<>();map1.put("s001", new Student("小红",24));map1.put("s002", new Student("小红",24));System.out.println(map1);//(2)}}
问题:(1)和(2)会打印成功吗?如果成功打印结果是什么?如果不成功说明原因

分析(1):采用了TreeMap的无参构造方法,键对象的类型是是自定义类型,从Student类的方法来看,未重写Comparable的compareTo方法,无法进行比较,会出现java.lang.ClassCastException,底层源码可知,无法从Student转换成Comparator的父类

分析(2):首先要明确一点,一旦使用TreeMap集合,就必须给其排序(不管是外部还是内部的构造器),具体使用哪种构造器看是否有关构造方法;在这里由于TreeMap是无参的,键对象是String类型,默认对键对象调用compareTo方法,键对象排序,所以显然能排序成功,那么打印的是什么呢?

首先明确一点,TreeMap集合重写了toString()方法,底层实现为{键对象1的toString=值对象1的toString(),键对象2的toString=值对象2的toString()...},但由于Student没有重写toString()方法,默认继承Object的toString()方法,所以值对象的toString()打印的是地址,如果不想打印地址怎么办?必须重写toString()方法。

那么问题又来了(问题怎么这么多),如果是HashMap类型,能否将上述两个元素(1)加入到集合里

实例6 

package afternoon;import java.util.HashMap;public class Demo1 {public static void main(String[] args) {HashMap<Student, String> map = new HashMap<Student,String>();//自然必须map.put(new Student("小红",24), "s001");map.put(new Student("小红",24), "s002");System.out.println("集合元素个数:"+map.size());System.out.println(map);}}
问题:元素的个数是多少?打印值是多少?

因为:HashMap添加元素的时候,底层是hash表,首先调用键值类型的HashCode()方法,由于自定义的Student没有重写此方法,会去继承Object的的HashCode()方法返回的是对象的地址,两个地址当然不一样了(又不是储存在在常量池),连HashCOde()过不了,更别说到equals()方法了------解决hash冲突

下一节:hashMap的扩容机制(重点)以及Map集合的遍历形式,斗地主发牌洗牌,集合潜逃的深入了解,相关;练习











阅读全文
0 0
原创粉丝点击