【传智播客上海校区】TreeMap源码解析

标签: 黑马程序员 上海校区 传智播客 Java
11人阅读 评论(0) 收藏 举报

案例剖析

关于Map集合的特点,我们都知道底层数据结构是控制Map集合的键,而和值即value是无关的。通过查阅JDK的API我们发现TreeMap集合的底层是基于红黑树的即二叉树。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的Comparator进行排序,具体取决于使用的构造方法。如果使用的构造方法是TreeMap()那么底层的键就是自然排序,如果使用的构造方法是TreeMap(Comparator<? super K> comparator) ,那么底层的键就是Comparator进行排序的。

接下来我们先看如下案例:

向TreeMap集合中存储数据,要求键为自定义Student类的对象,家庭住址作为TreeMap的值,类型是String类型。

学生Student类代码如下:

packagecn.itcast.sh.set;/** 描述学生*/publicclassStudent {//属性Stringname;intage;publicStudent(Stringname, intage) {this.name=name;this.age=age;}@OverridepublicStringtoString() {return"Student [name="+name+", age="+age+"]";}}

测试类TreeMapDemo01代码如下:

publicclassTreeMapDemo01 {publicstaticvoidmain(String[] args) {// 创建集合对象TreeMap<Student, String>tm=newTreeMap<Student,String>();//添加数据tm.put(newStudent("张三",18), "上海");tm.put(newStudent("李四",19), "北京");tm.put(newStudent("王五",18), "深圳");tm.put(newStudent("张三",18), "上海");//获取所有的键Set<Student>keys=tm.keySet();//遍历Set集合for (Studentkey : keys) {//输出数据System.out.println(key+"----"+tm.get(key));}}}

上述代码运行结果会报如下错误:

分析异常的原因:

通过以上报异常错误原因大概知道我们要将自己定义的类Student的存储到TreeMap集合中的时候,发生自定义类Student不能被转换到Comparable比较器接口的异常,我们在代码中明明没有书写和Comparable接口相关的代码啊,那怎么会报这个异常呢?

这里我们需要查看一下TreeMap底层的源代码进行进一步分析。

关于TreeMap集合的深入分析

问题1:为什么集合中存储的元素可以不重复呢?

TreeMap集合底层使用了二叉树结构,在存储元素时,拿存储的元素会和树结构中已经存在元素进行比较大小(结果有三种:大于、小于、相等)。当比较的结果相等时,表示该元素已经存在了,则不存储。

问题2:为什么TreeMap集合中存储的元素会排序呢?

在存储元素时,先拿要存储的元素和树结构中已经存在的元素进行比较大小,如果要存储的元素大于树结构中已存在的元素,则把要存储的元素存放比较元素的右边;如果要要存储的元素小于树结构中已存在的元素,则把要存储的元素放在左边。

分析TreeMap集合的源码查找为什么会报上述异常:

当上述代码中我们使用集合对象tm调用put方法向集合添加数据时,报了异常。所以我们可以查看put方法的源代码:

TreeMap集合中的put方法源代码如下所示:

publicVput(Kkey, Vvalue) {Entry<K,V>t=root;//表示二叉树的根元素if (t==null) {compare(key, key); // type (and possibly null) checkroot=newEntry<>(key, value, null);size=1;modCount++;returnnull; }intcmp;//记录比较的结果,有三种结果 大 小 相等Entry<K,V>parent;// split comparator and comparable pathsComparator<?superK>cpr=comparator;//自定义比较器对象if (cpr!=null) {//判断自定义比较器对象是否为null,由于我们在创建TreeMap集合的时候根本就没有传递自定义比较器对象,所以cpr是null,那么这里false,所以不会执行if中的代码,执行elsedo {parent=t;cmp=cpr.compare(key, t.key);if (cmp<0)t=t.left;elseif (cmp>0)t=t.right;elsereturnt.setValue(value); } while (t!=null); }else {if (key==null)thrownewNullPointerException();Comparable<?superK>k= (Comparable<?superK>) key;do {parent=t;cmp=k.compareTo(t.key);if (cmp<0)t=t.left;elseif (cmp>0)t=t.right;elsereturnt.setValue(value); } while (t!=null); }Entry<K,V>e=newEntry<>(key, value, parent);if (cmp<0)parent.left=e;elseparent.right=e;fixAfterInsertion(e);size++;modCount++;returnnull; }

对于上述源代码的解释:

Entry<K,V> t = root;//表示二叉树的根元素

if (t==null) {//判断根元素t是否为空,在插入第一个数据之前根元素肯定是nullcompare(key, key); // type (and possibly null) checkroot=newEntry<>(key, value, null);size=1;modCount++;returnnull; }compare(key, key);比较key,其实这里是判断key位置对象所属类即Student是否实现Comparable接口。代码如下:```javafinal int compare(Object k1, Object k2) { return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2) : comparator.compare((K)k1, (K)k2); }

由于我们在创建TreeMap对象时并没有传递自定义比较器Comparator的对象,所以comparator等于null。这里会执行 ((Comparable<? super K>)k1).compareTo((K)k2)。这句话的意思是对两个键进行比较,按照自然排序Comparable接口中的compareTo进行排序,但是必须将传递进来的Student对象进行强转。但是Student根本就没有实现Comparable接口,所以这里会报类转换异常。为了解决上述错误,只需Student实现Comparable接口即可。

root = new Entry<>(key, value, null); 表示创建根元素对象,即将第一个元素添加到二叉树的根元素位置。

由于在创建TreeMap集合对象时并没有传递自定义比较器Comparator的对象,所以自定义比较器对象是null,执行如下代码:

else {if (key==null)//在TreeMap集合中key不能是null,只要是null就会报空指针异常thrownewNullPointerException();Comparable<?superK>k= (Comparable<?superK>) key;//将添加到集合中的key强转为Comparable类型do {parent=t;cmp=k.compareTo(t.key);if (cmp<0)t=t.left;elseif (cmp>0)t=t.right;elsereturnt.setValue(value); } while (t!=null); }

cmp = k.compareTo(t.key);

t.key表示根元素的键,k表示后添加的键,按照自然排序进行比较。将返回值放到cmp变量中。

如果cmp变量小于0,将执行t = t.left;如果cmp变量大于0,将执行 t = t.right;如果cmp等于0,后添加的值将之前的值覆盖。

这里由于 t.left或者t.right初始化值都是null,所以t等于null。

执行如下代码:

Entry<K,V>e=newEntry<>(key, value, parent);if (cmp<0)parent.left=e;//将元素放到左子树elseparent.right=e;//将元素放到右子树fixAfterInsertion(e);//进行反转size++;modCount++;returnnull;


查看评论

揭秘传智播客上海校区惊人高薪和高就业率背后的”真相”

揭秘传智播客上海校区惊人高薪和高就业率背后的”真相”               众所周知,近几年IT行业发展势头迅猛,成为目前最具前景的高薪行业之一,同时也被喻为“朝阳产业”,这也是很多人...
  • qq_39332786
  • qq_39332786
  • 2017-08-22 12:01:41
  • 1229

传智播客上海校区“大神季”腾讯课堂各学科公开课直播地址!

传智播客上海校区“大神季”腾讯课堂各学科公开课直播地址! 同学们,送福利来了! 是不是很好奇其他学科的同学都在学什么? 人工智能与Python到底是什么? Ja...
  • qq_39332786
  • qq_39332786
  • 2018-01-04 15:22:41
  • 188

好消息!传智播客武汉分校校址已确定!!!

传智播客武汉分校校址已选定,地址在珞喻路与珞狮路交叉路口东北角的2号线“街道口”站的C出口楼上403室。 周围的标志性建筑:新世纪百货、群光广场、省妇幼保健院、武汉大学信息学部。 注意:C出口所在的楼...
  • itcast_cn
  • itcast_cn
  • 2013-12-30 10:56:50
  • 3724

尚硅谷——Java TreeMap源码解析

  • 2018年01月31日 10:53
  • 486KB
  • 下载

武汉学员,我们和传智播客boss在一起

我和你一样,一样的坚强,一样的全力以赴追逐我的梦想,哪怕会受伤,哪怕有风浪,风雨之后才会有迷人的芬芳;我们为自己代言,我们是08期Android就业班的全体学员,我们为自己加油!...
  • itcast_cn
  • itcast_cn
  • 2015-07-24 15:17:47
  • 1413

【传智播客郑州校区分享】在传智播客郑州校区学习倍感幸运

小李是传智播客郑州校区的一名学员,他大学学的是土木工程,当初报这个专业是觉得毕业后比较好找工作,然而经历四年的学习后才发现很多事情并不是自己想象的那样,但是刚毕业的小李还是不得不去从事与专业相关的工作...
  • qq_40208605
  • qq_40208605
  • 2018-01-25 11:16:10
  • 89

【Java集合源码剖析】TreeMap源码剖析

前言 本文不打算延续前几篇的风格(对所有的源码加入注释),因为要理解透TreeMap的所有源码,对博主来说,确实需要耗费大量的时间和经历,目前看来不大可能有这么多时间的投入,故这里意在通过于...
  • mmc_maodun
  • mmc_maodun
  • 2014-07-07 07:47:49
  • 10752

java集合(6):TreeMap源码分析(jdk1.8)

前言TreeMap的基本概念: TreeMap集合是基于红黑树(Red-Black tree)的 NavigableMap实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Compar...
  • qq_32166627
  • qq_32166627
  • 2017-05-26 20:24:14
  • 1402

大学毕业生在传智播客郑州校区完美蜕变

小谢大学毕业后参加了传智播客郑州校区的培训,他用自己的励志经历诠释了:只要有梦想,只要能坚持,哪怕有些迟,但一定能达成所愿。 小谢说大学三年他是带着后悔与懊恼度过的,期间充满着迷茫与悲伤,还有疯...
  • qq_40208605
  • qq_40208605
  • 2017-11-20 14:22:22
  • 95
    个人资料
    持之以恒
    等级:
    访问量: 4969
    积分: 312
    排名: 25万+
    文章分类