TreeMap
学习完毕后,整个的 Map集合
也就学习完毕了。
在整个Map体系中,其实还有 Hashtable
、Properties
这两个集合,由于这两个集合会有跟 IO 相关的方法,因此当我们学完IO后再去学习这两个集合。
关于 TreeMap
我们同样没有什么额外的新的方法学习,只需要知道它本身的特点和底层原理就行了。
一、特点
TreeMap
跟 TreeSet
底层原理一样,都是红黑树结构。
TreeMap
所有的特点也是由键决定的:不重复、无索引、可排序(对键进行排序)。
PS:默认按照键的从小到大进行排序,也可以自己规定键的排序规则。
代码的书写跟 TreeSet
是一样的
- 实现
Comparable接口
,指定比较规则。 - 创建集合时传递
Comparator
比较器对象,指定比较规则。
如果第一种和第二种同时存在,那么以第二种为准。
二、练习一
需求1:
键:整数表示id
值:字符串表示商品名称
要求1:按照id的升序排列
要求2:按照id的降序排列
运行以下代码,发现它默认是按照 id
升序排列的
//1.创建集合对象
TreeMap<Integer,String> tm = new TreeMap<>();
//2.添加元素
tm.put(5,"可恰可乐");
tm.put(4,"雷碧");
tm.put(3,"九个核桃");
tm.put(2,"康帅傅");
tm.put(1,"粤利粤");
//3.打印集合
System.out.println(tm); // {1=粤利粤, 2=康帅傅, 3=九个核桃, 4=雷碧, 5=可恰可乐}
跟进 Integer
,可以发现它也是实现了 Comparable接口
的。
我们找到它重写的 compareTo
方法,可以发现在这个里面它其实已经制定了排序的规则,这个规则就是升序排列
跟进 compare()
,可以发现这个方法里面就是一个三元运算符。
此时我们就知道了 Integer
、Double
等默认都是按照升序排列的。而我们在这里存的键就是 Integer
,因此它就默认按照升序排列了。
String
是按照字母在 ASCII码表
中对应的数字升序进行排列的,而且是从首字母开始,一个一个往后比较。
题目还有第二个需求:按照 id
降序排列。此时默认的方式就已经不能满足我们的需求了,就可以使用第二种方式:在创建集合对象的时候,小括号中可以传递比较器的对象。
TreeMap<Integer,String> tm = new TreeMap<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
//o1:当前要添加的元素
//o2:表示已经在红黑树中存在的元素
return o2 - o1;
}
});
三、练习二
需求2:
键:学生对象
值:籍贯
要求:按照学生年龄的升序排列,年龄一样按照姓名的字母排列,同姓名年龄视为同一个人。
测试类
//1.创建集合
TreeMap<Student,String> tm = new TreeMap<>();
//2.创建三个学生对象
Student s1 = new Student("zhangsan",23);
Student s2 = new Student("lisi",24);
Student s3 = new Student("wangwu",25);
//3.添加元素
tm.put(s1,"江苏");
tm.put(s2,"天津");
tm.put(s3,"北京");
//4.打印集合
System.out.println(tm);
如果不指定排序规则,就会直接报错。
Student.java
public class Student implements Comparable<Student>{
private String name;
private int age;
// 无参构造、有参构造、get、set方法
@Override
public int compareTo(Student o) {
//按照学生年龄的升序排列,年龄一样按照姓名的字母排列,同姓名年龄视为同一个人。
//this:表示当前要添加的元素
//o:表示已经在红黑树中存在的元素
//返回值:
//负数:表示当前要添加的元素是小的,存左边
//正数:表示当前要添加的元素是大的,存右边
//0:表示当前要添加的元素已经存在,舍弃
int i = this.getAge() - o.getAge();
i = i == 0 ? this.getName().compareTo(o.getName()) : i;
return i;
}
}
四、利用 TreeMap
进行排序
需求:
字符串“aababcabcdabcde”
请统计字符串中每一个字符出现的次数,并按照以下格式输出
输出结果:
a(5)b(4)c(3)d(2)e(1)
由于利用计数器的方式进行统计有一定的弊端,因此我们就引出来一个新的思想:利用 Map集合
进行统计。
利用 Map集合
进行统计有两种方式:1、HashMap
;2、TreeMap
它们的键都表示:要统计的内容;值:表示要统计的次数。
如果题目中没有要求对结果进行排序,默认使用 HashMap
,因为它的效率是最高的。
但是如果题目要求对结果进行排序,请使用 TreeMap
。
由结果可知,这里应该使用 TreeMap
。
//1.定义字符串
String s = "aababcabcdabcde";
//2.创建集合
TreeMap<Character,Integer> tm = new TreeMap<>();
//3.遍历字符串得到里面的每一个字符
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
//拿着c到集合中判断是否存在
//存在,表示当前字符又出现了一次
//不存在,表示当前字符是第一次出现
if(tm.containsKey(c)){
//存在
//先把已经出现的次数拿出来
int count = tm.get(c);
//当前字符又出现了一次
count++;
//把自增之后的结果再添加到集合当中
tm.put(c,count);
}else{
//不存在
tm.put(c,1);
}
}
//4.遍历集合,并按照指定的格式进行拼接
// a(5)b(4)c(3)d(2)e(1)
//StringBuilder sb = new StringBuilder();
//tm.forEach((key, value)->sb.append(key).append("(").append(value).append(")"));
StringJoiner sj = new StringJoiner("","","");
// 非字符串的内容需要 + ""
tm.forEach((key, value)->sj.add(key + "").add("(").add(value + "").add(")"));
System.out.println(sj);
五、总结
1、TreeMap集合
的特点是什么样的?
- 键:不重复、无索引、可排序
- 底层基于红黑树进行实现,增删改查性能较好
2、TreeMap集合
排序的两种方式
- 默认的排序规则:实现
Comparable接口
,指定比较规则 - 创建集合时传递Comparator比较器对象,指定比较规则
例如字符串、Integer中默认的排序规则有时候就不能满足我们的需求了。