学习任务:

视频学习

升序排序测试

降序排序测试

稍微复杂的排序规则

相等判断

Map的key和value是Object类型,所以key可以为一个自定义的类型,比如:key为PbStudent类型,value为PbCourse类型,这样可以实现通过一个PbStudent对象找到选择的课程。

image-20210714122730117

按照TreeMap的要求key必须实现Comparable接口,实现compareTo方法,本次排序实现按学生id升序,key为PbStudent类型,所以PbStudent类型要实现Comparable接口。

根据上节对Integer类的compareTo方法的分析,它有三种返回值,如下:

1、新数大于旧数,返回大于0的数。

2、新数小于旧数,返回小于0的数。

3、新数与旧数相等,返回等于0的数,新数的value值替换旧数的value值。

按照上边的逻辑实现compareTo方法最终实现升序。

下边实现按学生的年龄升序排序,根据compareTo方法的三种返回值,compareTo方法定义如下:

	public int compareTo(PbStudent o) {
		if(this.getId()>o.getId()){
			return 1;
		}else if(this.getId()<o.getId()){
			return -1;
		}else{
			return 0;
		}
	}

可以优化如下:

	public int compareTo(PbStudent o) {
		return this.getId()-o.getId();
	}

说明:this.getId()-o.getId()表示拿新数的id减去旧数的id,如果大于0则说明新数大于旧数。

代码如下:

package com.pbteach.javase.oop.map.test1;

/**
 * 学生类型
 * @author 攀博课堂(www.pbteach.com)
 *
 */
public class PbStudent implements Comparable<PbStudent>{
	//用户id
	public Integer id;
	//昵称
	private String nickname;
	//年龄
	private Integer age;
	public PbStudent(Integer id,String nickname,Integer age){
		this.id = id;
		this.nickname = nickname;
		this.age = age;
	}
	@Override
	public int compareTo(PbStudent o) {
		return this.getId()-o.getId();
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getNickname() {
		return nickname;
	}
	public void setNickname(String nickname) {
		this.nickname = nickname;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "PbStudent{" +
				"id='" + id + '\'' +
				", nickname='" + nickname + '\'' +
				", age=" + age +
				'}';
	}
}

测试代码如下:

public static void sort3() {
		//定义一个map
		Map<PbStudent,PbCourse> map = new TreeMap<>();

        //添加元素,key为PbStudent类型,value为PbCourse类型
		PbStudent pbStudent0 =new PbStudent(100, "ab", 19);
		PbStudent pbStudent1 =new PbStudent(101, "bc", 19);
		PbStudent pbStudent2 =new PbStudent(102, "ac", 20);
		PbStudent pbStudent3 =new PbStudent(103, "ba", 20);
		PbCourse pbCourse0 = new PbCourse(100L, "攀博课堂-Java面向对象教程", 33);
		PbCourse pbCourse1 = new PbCourse(101L, "攀博课堂-Spring全家桶教程", 44);
		PbCourse pbCourse2 = new PbCourse(102L, "攀博课堂-SpringCloud微服务教程", 55);
		PbCourse pbCourse3 = new PbCourse(103L, "攀博课堂-在线教育分布式项目教程", 66);
		map.put(pbStudent0,pbCourse0);
		map.put(pbStudent1,pbCourse1);
		map.put(pbStudent2,pbCourse2);
		map.put(pbStudent3,pbCourse3);

		map.forEach((key,value)->System.out.println(key+"  "+value));

	}

输出:

PbStudent{id='100', nickname='ab', age=23}  PbCourse [id=100, courseName=攀博课堂-Java面向对象教程, price=33]
PbStudent{id='101', nickname='bc', age=19}  PbCourse [id=101, courseName=攀博课堂-Spring全家桶教程, price=44]
PbStudent{id='102', nickname='ac', age=24}  PbCourse [id=102, courseName=攀博课堂-SpringCloud微服务教程, price=55]
PbStudent{id='103', nickname='ba', age=20}  PbCourse [id=103, courseName=攀博课堂-在线教育分布式项目教程, price=66]

从输出结果可以看出是按学生ID升序排列。

如何实现降序排序?只要把compareTo方法的逻辑改为如下即可:

	public int compareTo(PbStudent o) {
		if(this.getId()>o.getId()){
			return -1;
		}else if(this.getId()<o.getId()){
			return 1;
		}else{
			return 0;
		}
	}

可以优化如下:

	public int compareTo(PbStudent o) {
		return o.getId()-this.getId();
	}

解读如下:

TreeMap根据compareTo的方法返回值确定将新数添加到集合的位置,每次拿新数和集合中的旧数比较,根据compareTo方法的返回决定新数放在旧数的前边还是后边,按默认的自然排序规则,后边的数大于前边的数,当返回大于0的数时新数放在旧数的后边,返回小于0的数时新数放在旧数的前边。

所以,升序的逻辑是新数大于旧数返回大于0的数,将新数放在旧数的后边。

if(this.getId()>o.getId()){
	return 1;
}

降序的逻辑是新数大于旧数返回小于0的数,将新数放在旧数的前边。

if(this.getId()>o.getId()){
	return -1;
}

更改PbStudent类的compareTo方法测试降序及升序的效果。

下边思考一个需求的实现方案,选按学生的年龄升序排序,如果年龄相等则根据学生id进行升序排序。

根据上边的程序实现可知,排序的规则定义在PbStudent类中的compareTo方法,如下:

@Override
	public int compareTo(PbStudent o) {
		return o.getAge()-this.getAge() ;//按年龄降序
	}

如果要修改排序规则为“如果年龄相等则根据学生id进行升序排序”,修改如下:

	@Override
	public int compareTo(PbStudent o) {
		//先按年龄降序,如果年龄相等则根据学生id进行升序排序
		int r= o.getAge()-this.getAge();
		return r!=0?r:this.getId()-o.getId();
	}

向TreeMap添加元素,如果key相等则用新元素的value值替换相同key的旧元素的value值,这个效果在上节课中使用Integer类型的key进行测试通过。

对于TreeMap中的key不伦是基本类型还是引用类型都是根据Comparable接口的compareTo方法判断key是否相等,只要compareTo方法返回0就认为两个key相等。

拿上边定义的排序规则“如果年龄相等则根据学生id进行升序排序”举例,什么情况会返回0?当学生的年龄和学生的Id都相等时compareTo方法返回0,此时用新元素的value值替换相同key的旧元素的value值。

TreeMap的put和get两个方法采用上边的规则判断两个key是否相等。

测试如下:

//相等判断
	public static void sort3_1() {
		//定义一个map
		Map<PbStudent,PbCourse> map = new TreeMap<>();

        //添加元素,key为PbStudent类型,value为PbCourse类型
		PbStudent pbStudent0 =new PbStudent(100, "ab", 19);
		PbStudent pbStudent1 =new PbStudent(101, "bc", 19);
		PbStudent pbStudent2 =new PbStudent(102, "ac", 20);
		PbStudent pbStudent3 =new PbStudent(103, "ba", 20);
		PbStudent pbStudent4 =new PbStudent(103, "ba", 20);
		PbCourse pbCourse0 = new PbCourse(100L, "攀博课堂-Java面向对象教程", 33);
		PbCourse pbCourse1 = new PbCourse(101L, "攀博课堂-Spring全家桶教程", 44);
		PbCourse pbCourse2 = new PbCourse(102L, "攀博课堂-SpringCloud微服务教程", 55);
		PbCourse pbCourse3 = new PbCourse(103L, "攀博课堂-在线教育分布式项目教程", 66);
		PbCourse pbCourse4 = new PbCourse(104L, "攀博课堂-支付系统教程", 77);
		map.put(pbStudent0,pbCourse0);
		map.put(pbStudent1,pbCourse1);
		map.put(pbStudent2,pbCourse2);
		map.put(pbStudent3,pbCourse3);
		map.put(pbStudent4,pbCourse4);//这里向map中添加了相同的key

		map.forEach((key,value)->System.out.println(key+"  "+value));

	}

输出:

PbStudent{id='102', nickname='ac', age=20}  PbCourse [id=102, courseName=攀博课堂-SpringCloud微服务教程, price=55]
PbStudent{id='103', nickname='ba', age=20}  PbCourse [id=104, courseName=攀博课堂-支付系统教程, price=77]
PbStudent{id='100', nickname='ab', age=19}  PbCourse [id=100, courseName=攀博课堂-Java面向对象教程, price=33]
PbStudent{id='101', nickname='bc', age=19}  PbCourse [id=101, courseName=攀博课堂-Spring全家桶教程, price=44]

说明:

虽然新添加的元素key是新的对象pbStudent4,但是pbStudent4对象的学生年龄和学生id与pbStudent3一致,所以用新元素的value值替换相同key的旧元素的value值。

注意:TreeMap 的put操作和get操作是根据compareTo方法进行判断,并不是根据equals、hashCode方法进行判断。

提问-攀博课堂
我要提问 不会就问,有效沟通
关注公众号,加入微信群交流提问。 攀博课堂官方公众号
问答列表,查看本知识点所有问题