博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java中TreeMap集合讲解
阅读量:5104 次
发布时间:2019-06-13

本文共 4743 字,大约阅读时间需要 15 分钟。

1.TreeSet介绍

TreeSet是一个有序集合,可以以任意顺序将元素插入到集合中,在对集合进行遍历的时候,每个元素将自动按照排序后的顺序呈现。底层使用的是二叉树(更具体点是红黑树)实现,对于元素之间排序,如果不指定自定义的比较器Comparator,那么插入的对象必须实现Comparable接口,元素按照实现此接口的compareTo()方法去排序。如果指定了自定义的比较器Comparator,优先使用Comparator去对元素进行排序。比较规则决定了元素是否可以重复,以及元素的排序结果。

2.特点

  • 保存的元素按照一定的规则有序
  • 可自定义排序规则
  • 非线程安全

3.存储Integer类型元素

import java.util.TreeSet;public class DemoTreeSet {	public static void main(String[] args) {		TreeSet
ts = new TreeSet<>(); ts.add(1); //自动装箱 ts.add(7); ts.add(3); ts.add(5); ts.add(3); //重复元素 System.out.println(ts); }}输出:[1, 3, 5, 7]

看上面代码的输出,不仅输出元素有序,而且去除了重复元素3,我们在存元素的时候,并不是按照1,3,5,7的顺序存储的,但是遍历却是按照顺序排列的。为什么呢?

前面我们说过,TreeSet在存对象的时候,如果不指定自定义的比较器,那么存储的对象必须实现Comparable接口,好,我们去看下Integer类有没有实现此接口。

public final class Integer extends Number implements Comparable
{ //省略其他代码 //...... public int compareTo(Integer anotherInteger) { return compare(this.value, anotherInteger.value); } public static int compare(int x, int y) { return (x < y) ? -1 : ((x == y) ? 0 : 1); }}

分析:     

    通过Integer源码可知,它满足TreeSet的要求,实现了Comparable接口中唯一的抽象方法compareTo(),在此方法内部又调用了一个compare()方法去定义比较规则,返回值是0,1,或者-1。前面说过,TreeSet底层是二叉树实现的,当存储元素的时候,会调用比较规则方法和二叉树上的元素一一比较,如果要插入的元素比当前元素小就到左子树去比较,如果比当前元素大,就到右子树去比较,直到当前元素的左或者右子树为空,就插入此元素。如果在比较过程中,出现当前元素等于要插入的元素,那么此元素不插入,例如上例中最后一个元素3被过滤掉了,这样也就保证了Set的元素唯一性。

自定义比较器    

    前面说过,如果我们自定义了比较器Comparator,那么集合会优先使用自定义的比较器去对元素进行排序,好,我们现在想让集合中的Integer元素按照倒序排列,应该如何实现呢?先来查看JDK文档:

上图中红框标注的构造方法就是我们需要的,这里需要传入一个Comparator接口类型的对象,我们当然可以自定义一个类然后实现Comparator接口,然后再new一个对象当作参数传入TreeSet()的构造方法,但是为了方便,我们使用匿名类实现。在此之前,我们先看下Comparator接口都有什么抽象方法。

此接口只有两个抽象方法,我们先不用管equals()方法,定义排序规则需要在compare()这个方法。好,我们来实现一下集合中元素倒序排序。

import java.util.Comparator;import java.util.TreeSet;public class DemoTreeSet {	public static void main(String[] args) {		TreeSet
ts = new TreeSet<>(new Comparator
() { @Override public int compare(Integer o1, Integer o2) { if(o1 == o2) return 0; return o1 < o2 ? 1 : -1; } }); ts.add(1); //自动装箱 ts.add(7); ts.add(3); ts.add(5); ts.add(3); System.out.println(ts); }}输出:[7, 5, 3, 1]

分析: 

1、读者可能发现,Comparator接口明明有两个抽象方法,为什么只实现了compare(),很简单。我们知道匿名类也是一个类,只要是类就会默认继承Object类,那么就会默认继承Object类的equals()方法,所以其实我们已经使用了从Object类继承的方法去实现了Comparator接口的equals()方法。

2、我们在compare()方法中定义了排序规则,和Integer类中实现的方式正好相反,小于返回1,大于返回-1,等于返回0,所以输出的元素就是倒序了。由于我们在TreeSet中自定义了比较器,所以Integer类的比较规则就失效了。

4.存储自定义对象

Student类
public class Student implements Comparable
{ private String name; private int age; public Student(String name, int age) { super(); this.name = name; this.age = age; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } @Override public int compareTo(Student o) { return this.age - o.age; }}
import java.util.TreeSet;public class DemoTreeSet {	public static void main(String[] args) {		TreeSet
ts = new TreeSet<>(); ts.add(new Student("张廷玉",43)); ts.add(new Student("纳兰明珠",80)); ts.add(new Student("索额图",79)); System.out.println(ts); }}输出:[Student [name=张廷玉, age=43], Student [name=索额图, age=79], Student [name=纳兰明珠, age=80]]

    首先我们定义了一个Student类,前面说过,如果没有自定义比较器,TreeSet集合存储的对象元素必须实现Comparable接口,这里我们的Student类实现了此接口,并且实现了此接口的唯一抽象方法compareTo(),这里我们是按照学生年龄排序(升序)。如果这里不实现Comparable接口,那么会报 java.lang.ClassCastException异常,意思就是这里存的元素不是Comparable类型的。

自定义比较器    

我们用自定义比较器来实现一个小案例。这里我们对学生首先按照年龄进行排序,如果年龄相同,我们再按照名字的长度进行排序,都是升序,来看看我们怎么实现它。

public class Student{	private String name;	private int age;	public Student(String name, int age) {		super();		this.name = name;		this.age = age;	}	@Override	public String toString() {		return "Student [name=" + name + ", age=" + age + "]";	}	public String getName() {		return name;	}	public int getAge() {		return age;	}}
import java.util.Comparator;import java.util.TreeSet;public class DemoTreeSet {	public static void main(String[] args) {		TreeSet
ts = new TreeSet<>(new Comparator
() { @Override public int compare(Student o1, Student o2) { int num = o1.getAge() - o2.getAge(); return num == 0 ? o1.getName().length() - o2.getName().length() : num; } }); ts.add(new Student("张廷玉",43)); ts.add(new Student("纳兰明珠",80)); ts.add(new Student("索额图",79)); ts.add(new Student("苏麻喇姑",79)); for (Student student : ts) { System.out.println(student); } }}输出:Student [name=张廷玉, age=43]Student [name=索额图, age=79]Student [name=苏麻喇姑, age=79]Student [name=纳兰明珠, age=80]

由于我们在TreeSet中自定义了比较器,这里的Student类不需要实现Comparable接口了。我们来看compare()方法内部,首先计算比较的元素的年龄差num,如果num不等于0,那么我们还是按照年龄排序,如果年龄相同,那么我们返回元素名字长度的差,也就是说年龄相同,我们按照名字长度进行排序。从输出可以看出,索额图和苏麻喇姑的年龄相同,但是索额图名字长度要小于苏麻喇姑,所以索额图排在了苏麻喇姑的前面输出。

至此!如何使用TreeSet去存储对象以及内部的排序原理我们就讲完了,有时间自己动手去实现一下,多思考为什么要这样,眼高手低是程序员一大忌讳。如果不懂的读者可以在下面留言!

转载于:https://www.cnblogs.com/neuzk/p/9476418.html

你可能感兴趣的文章
收集WebDriver的执行命令和参数信息
查看>>
数据结构与算法(三)-线性表之静态链表
查看>>
mac下的mysql报错:ERROR 1045(28000)和ERROR 2002 (HY000)的解决办法
查看>>
快速幂
查看>>
改善C#公共程序类库质量的10种方法
查看>>
AIO 开始不定时的抛异常: java.io.IOException: 指定的网络名不再可用
查看>>
MyBaits动态sql语句
查看>>
HDU4405(期望DP)
查看>>
拉格朗日乘子法 那些年学过的高数
查看>>
vs code 的便捷使用
查看>>
Spring MVC @ResponseBody返回中文字符串乱码问题
查看>>
用户空间与内核空间,进程上下文与中断上下文[总结]
查看>>
JS 中的跨域请求
查看>>
JAVA开发环境搭建
查看>>
mysql基础语句
查看>>
Oracle中的rownum不能使用大于>的问题
查看>>
[Data Structure & Algorithm] 有向无环图的拓扑排序及关键路径
查看>>
cassandra vs mongo (1)存储引擎
查看>>
Visual Studio基于CMake配置opencv1.0.0、opencv2.2
查看>>
遍历Map对象
查看>>