5.5(java学习笔记)TreeSet和TreeMap
1.TreeMap
TreeMap是可排序的Map类,使用这个类时,TreeMap会对存放的数据进行排序。
排序是根据key来排序的,排序规则是key实现comparable接口中的compareTo()方法
或指定一个排序规则类实现comparator接口中的compare()方法,将其实例化的对象传入Tree。
我们来看下Tree排序的执行过程。
TreeMap还有其他构造方法,我们就看这两两个。
第一个是没有使用排序规则类的构造方法,那么作为key必须实现了Comparable接口中的compareTo()方法。
第二个是使用了排序规则类的构造方法。
我们接着来看Tree中的put方法
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check root = new Entry<>(key, value, null);
size = ;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {//判断是否有排序规则,有就用排序规则进行排序
do {
parent = t;
cmp = cpr.compare(key, t.key);//实现了comparator接口的比较规则类中的compare方法
if (cmp < )
t = t.left;
else if (cmp > )
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else { //没有排序规则就使用key实现的compareTo方法比较
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do { //实现了Comparable接口中的compareTo方法
parent = t;
cmp = k.compareTo(t.key);
if (cmp < )
t = t.left;
else if (cmp > )
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < )
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
我们可以看到上面代码,首先判断是否有排序规则类的实例化对象,有就用这个对象中的compare方法,
没有就用key中的compareTo方法。
排序的依据是key。
TreeMap底层排序的实现是红黑树,有兴趣可以参阅:https://blog.csdn.net/a616413086/article/details/52586006
所以使用Tree排序,其中的Key必须实现一种接口(Comparable或Comparator都可以)
News类:
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date; public class News implements Comparable<News>{
private String newsName;
private Date date;
private int click;
private String timeS;
public News(){}
public News(String newsName, Date date, int click){
setNewsName(newsName);
setDate(date);
setClick(click);
}
public String getNewsName() {
return newsName;
} public void setNewsName(String newsName) {
this.newsName = newsName;
} public Date getDate() {
return date;
} public void setDate(Date date) {
this.date = date;
DateFormat china_time = new SimpleDateFormat("yyyy-MM-dd hh-mm-ss");
timeS = china_time.format(date); }
public int getClick() {
return click;
} public void setClick(int click) {
this.click = click;
}
public int compareTo(News o) {//用于比较的compareTo方法
// TODO Auto-generated method stub
if(this.getDate().after(o.getDate()))//先判断时间,按时间降序排列
return -1;
else if(this.getDate().getTime()-o.getDate().getTime()==0){//如果时间相同按点击量降序排序
if(this.getClick() > o.click)
return -1;
else if(this.getClick()==o.getClick())
return 0;
}
return 1; //
} public String toString(){
return "标题:" + this.newsName + "时间" + timeS
+ "点击量" + this.click + "\n";
}
}
TreeMap +Comparable +CompareTo实现排序
import javax.swing.text.html.HTMLDocument.Iterator; public class TestCompare {
public static void main(String[] args) {
Map<News,Object> newsMap = new TreeMap<>();//调用本身的compareTo方法
newsMap.put(new News("国庆高峰堵车,各景点爆满!",new Date(),1000),new Object());//设置为当前时间
newsMap.put(new News("国庆仍有大部分家里蹲!",new Date(System.currentTimeMillis()-60*60*1000),10000),new Object());//设置为当前时间前一小时
newsMap.put(new News("惊呆,游客竟在做这种事!!!",new Date(),5000),new Object());//设置为当前时间,点击量10000
Set<Map.Entry<News, Object>> set_m = newsMap.entrySet();//将Map封装成变为Set接口对象
java.util.Iterator<Entry<News, Object>> ite = set_m.iterator();//使用set接口中的迭代器
while(ite.hasNext()){
Map.Entry<News, Object> en = ite.next();//然后将其中的键取出。
System.out.println(en.getKey());
}
}
}
运行结果:
标题:惊呆,游客竟在做这种事!!!时间2018-10-14 10-50-58点击量5000 标题:国庆高峰堵车,各景点爆满!时间2018-10-14 10-50-57点击量1000 标题:国庆仍有大部分家里蹲!时间2018-10-14 09-50-58点击量10000
先按时间排序,再按点击量排序。
TreeMap + comparator+compare实现排序(News类中的Comparable及compareTo方法可以去掉也可以不去)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet; import javax.swing.text.html.HTMLDocument.Iterator; public class TestCompare {
public static void main(String[] args) {
Map<News,Object> newsMap = new TreeMap<>(new NewsClickSort());//将排序规则传递进去
newsMap.put(new News("国庆高峰堵车,各景点爆满!",new Date(),1000),new Object());//设置为当前时间
newsMap.put(new News("国庆仍有大部分家里蹲!",new Date(System.currentTimeMillis()-60*60*1000),10000),new Object());//设置为当前时间前一小时
newsMap.put(new News("惊呆,游客竟在做这种事!!!",new Date(),5000),new Object());//设置为当前时间,点击量10000
Set<Map.Entry<News, Object>> set_m = newsMap.entrySet();
java.util.Iterator<Entry<News, Object>> ite = set_m.iterator();
while(ite.hasNext()){
Map.Entry<News, Object> en = ite.next();
System.out.println(en.getKey());
}
}
} class NewsClickSort implements Comparator<News>{//排序规则类,按点击量降序 @Override
public int compare(News o1, News o2) {
if(o1.getClick() < o2.getClick()){
return 1;
}else{
return -1;
}
}
}
运行结果:
标题:国庆仍有大部分家里蹲!时间2018-10-14 10-11-51点击量10000 标题:惊呆,游客竟在做这种事!!!时间2018-10-14 11-11-51点击量5000 标题:国庆高峰堵车,各景点爆满!时间2018-10-14 11-11-51点击量1000
可以看到上面是按点击量降序排列。
2.TreeSet
理解了TreeMap之后理解TreeSet就简单了。
我们先来看下TreeSet的初始化构造函数和添加函数add()
一开始TreeSet直接创建一个TreeMap对象,只不过把键是需要存放的数据类型,值是一个Object对象。
我们接着来看add方法
直接调用TreeMap中的put方法,将元素放入键中,值里面放入一个Objcet对象。
后面就是调用TreeMap的代码了。
我们看下TreeSet的例子
运行函数:(使用compareTo进行排序)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet; public class TestCompare {
public static void main(String[] args) {
Set<News> newsSet = new TreeSet<>();
newsSet.add(new News("国庆高峰堵车,各景点爆满!",new Date(),1000));//设置为当前时间
newsSet.add(new News("国庆仍有大部分家里蹲!",new Date(System.currentTimeMillis()-60*60*1000),10000));//设置为当前时间前一小时
newsSet.add(new News("惊呆,游客竟在做这种事!!!",new Date(),5000));//设置为当前时间,点击量10000
System.out.println(newsSet);//TreeSet
}
}
运行结果://先比较时间(按时间降序排列),时间相同再比较点击量(按第点击量升序排列)
[标题:惊呆,游客竟在做这种事!!!时间2018-10-14 10-14-04点击量5000
, 标题:国庆高峰堵车,各景点爆满!时间2018-10-14 10-14-04点击量1000
, 标题:国庆仍有大部分家里蹲!时间2018-10-14 09-14-04点击量10000
]
我们可以看到上面结果是首先按时间降序,即最近发生的新闻品牌在最前面,时间相同时再按点击量排名。
运行函数:(使用compare进行排序)。News类与上面相同,可以去掉其中实现的comparable接口及compareTo方法,不去也可以。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet; public class TestCompare {
public static void main(String[] args) {
Set<News> newsSet = new TreeSet<>(new NewsClickSort());
newsSet.add(new News("国庆高峰堵车,各景点爆满!",new Date(),1000));//设置为当前时间
newsSet.add(new News("国庆仍有大部分家里蹲!",new Date(System.currentTimeMillis()-60*60*1000),10000));//设置为当前时间前一小时
newsSet.add(new News("惊呆,游客竟在做这种事!!!",new Date(),5000));//设置为当前时间,点击量10000
System.out.println(newsSet);//TreeSet
}
} class NewsClickSort implements Comparator<News>{//指定的排序规则类,按点击量排名 @Override
public int compare(News o1, News o2) {
if(o1.getClick() < o2.getClick()){//按点击量降序
return 1;
}else{
return -1;
}
}
}
运行结果://可以看到只是按点击量进行降序排列
[标题:国庆仍有大部分家里蹲!时间2018-10-14 09-21-36点击量10000
, 标题:惊呆,游客竟在做这种事!!!时间2018-10-14 10-21-36点击量5000
, 标题:国庆高峰堵车,各景点爆满!时间2018-10-14 10-21-35点击量1000
]
3.注意事项
TreeMap是在放入时比较,根据比较结果确定放入的位置。那么当数据放完后,再次修改已经放入的数据会怎么样,数据的位置会发生变化吗?
Person类
public class Person implements Comparable<Person>{
private String name;
private int age;
public Person(){}
public Person(String name,int age){
setName(name);
setAge(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;
}
public String toString(){
return "姓名:" + name + "年龄:" + age;
}
@Override
public int compareTo(Person o) {
if(this.getAge() > o.getAge())
return 1;
else if(this.getAge() == o.getAge())
return 0;
else
return -1;
} }
运行代码
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet; public class TestTreeMap {
public static void main(String[] args) {
Set<Person> s_per = new TreeSet<>();
Person p3 = new Person("张三", 30);
Person p4 = new Person("李四", 40);
Person p5 = new Person("王五", 50);
s_per.add(p5);
s_per.add(p3);
s_per.add(p4);
Iterator<Person> ite = s_per.iterator();//输出排序后顺序,(按年龄升序)
System.out.println("-------排序顺序-------");
while(ite.hasNext()){
System.out.println(ite.next());
}
p3.setAge(100);//修改张三年龄为100
System.out.println("-------修改年龄后-------");//输出修改后的顺序
ite = s_per.iterator();
while(ite.hasNext()){
System.out.println(ite.next());
}
}
}
运行结果:
-------排序顺序-------
姓名:张三年龄:30
姓名:李四年龄:40
姓名:王五年龄:50
-------修改年龄后-------
姓名:张三年龄:100
姓名:李四年龄:40
姓名:王五年龄:50
可以看到最后即使修改了张三的年龄,顺序依然没有变化,因为TreeSet是在添加时排序,
TreeSet是在添加数据时排序,后来修改它的值,不会影响原来的排序。
就像一开始确定张三在第一位,后来确定李四在第二位,然后确定王五在第三位,此时位置已经拍好了。
这时我修改张三的年龄,并不会改变位置,只会改变张三的年龄。
由此可见,TreeSet中排序发生在添加时,后面修改数据不会导致顺序出现变化。
TreeSet添加完数据后修改可能会导致数据重复,我们知道Set不能放入重复的数据,它只会在放入时检查。
一旦我们把数据放入了(放入的数据肯定是不会重复的)之后再次修改已经放入的数据可能会导致数据重复。
我们先来看下Map的,Set的也就很好理解了。
这个例子要在Person类中重写hashCode和equals方法,用于判断键是否重复。(前面为了简洁就没有重写,如果是自定义类要指定相等规则,即重写euqasl,hashCode)
public class Person implements Comparable<Person>{
private String name;
private int age;
public Person(){}
public Person(String name,int age){
setName(name);
setAge(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;
}
public String toString(){
return "姓名:" + name + "年龄:" + age;
}
@Override
public int compareTo(Person o) {
if(this.getAge() > o.getAge())
return 1;
else if(this.getAge() == o.getAge())
return 0;
else
return -1;
}
@Override //重写hashCode和equals方法,用于判断键是否重复
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;//如果是同一个对象肯定是相等的。
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;//Person类如果名字和年龄都相同就认为是同一个人。
} }
//运行函数
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet; public class TestTreeMap {
public static void main(String[] args) {
String s1 = "三";
String s2 = "四";
String s3 = "五";
Person p1 = new Person("张三",30);
Person p2 = new Person("李四",40);
Person p3 = new Person("王五",50);
Person p4 = new Person("李四",40);
Map<Person,Object> m = new HashMap<>();
m.put(p1, new Object());
m.put(p2, new Object());
m.put(p3, new Object());
m.put(p4, new Object());
System.out.println("---放入时会检查重复的key---");
Set<Map.Entry<Person, Object>> m_s = m.entrySet();
Iterator<Map.Entry<Person, Object>> ite = m_s.iterator();
while(ite.hasNext()){
Map.Entry<Person, Object> en = ite.next();
System.out.println(en.getKey());
}
p1.setAge(40);
p1.setName("李四");
ite = m_s.iterator();
System.out.println("---将张三修改为李四后---");
while(ite.hasNext()){
Map.Entry<Person, Object> en = ite.next();
System.out.println(en.getKey());
}
}
}
运行结果:
---放入时会检查重复的key---
姓名:王五年龄:50
姓名:张三年龄:30
姓名:李四年龄:40
---将张三修改为李四后---
姓名:王五年龄:50
姓名:李四年龄:40
姓名:李四年龄:40
可以看到,一开始添加的时候排除了重复的李四,但是添加完后进行修改导致了元素重复。
TreeSet底层就是用的TreeMap的键,思想和上述一样,所以使用TreeMap和TreeSet拍完序后,
可能会导致元素重复,就违背了最初的不允许放入重复元素的思想。
可以用关键字final来限制修改数据,避免这一问题。
5.5(java学习笔记)TreeSet和TreeMap的更多相关文章
- Java学习笔记4
Java学习笔记4 1. JDK.JRE和JVM分别是什么,区别是什么? 答: ①.JDK 是整个Java的核心,包括了Java运行环境.Java工具和Java基础类库. ②.JRE(Java Run ...
- 《Java学习笔记(第8版)》学习指导
<Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...
- 20145330第五周《Java学习笔记》
20145330第五周<Java学习笔记> 这一周又是紧张的一周. 语法与继承架构 Java中所有错误都会打包为对象可以尝试try.catch代表错误的对象后做一些处理. 使用try.ca ...
- java学习笔记11--集合总结
java学习笔记系列: java学习笔记10--泛型总结 java学习笔记9--内部类总结 java学习笔记8--接口总结 java学习笔记7--抽象类与抽象方法 java学习笔记6--类的继承.Ob ...
- 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...
- 0035 Java学习笔记-注解
什么是注解 注解可以看作类的第6大要素(成员变量.构造器.方法.代码块.内部类) 注解有点像修饰符,可以修饰一些程序要素:类.接口.变量.方法.局部变量等等 注解要和对应的配套工具(APT:Annot ...
- Java学习笔记(04)
Java学习笔记(04) 如有不对或不足的地方,请给出建议,谢谢! 一.对象 面向对象的核心:找合适的对象做合适的事情 面向对象的编程思想:尽可能的用计算机语言来描述现实生活中的事物 面向对象:侧重于 ...
- 0032 Java学习笔记-类加载机制-初步
JVM虚拟机 Java虚拟机有自己完善的硬件架构(处理器.堆栈.寄存器等)和指令系统 Java虚拟机是一种能运行Java bytecode的虚拟机 JVM并非专属于Java语言,只要生成的编译文件能匹 ...
- 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用
垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...
- 0028 Java学习笔记-面向对象-Lambda表达式
匿名内部类与Lambda表达式示例 下面代码来源于:0027 Java学习笔记-面向对象-(非静态.静态.局部.匿名)内部类 package testpack; public class Test1{ ...
随机推荐
- 牛客 国庆七天乐 day1 L
https://www.nowcoder.com/acm/contest/201/L 题意:给你两条平行的直线和n个圆,在直线上面行走和在圆上和在圆内行走不需要耗费体力,除了这些区域外平面上经过任意两 ...
- codeforces 1015E1&&E2
E1. Stars Drawing (Easy Edition) time limit per test 3 seconds memory limit per test 256 megabytes i ...
- Kafka配置文档
http://kafka.apache.org/08/configuration.html
- js中Date()对象详解
var myDate = new Date(); myDate.getYear(); //获取当前年份(2位) myDate.getFullYear(); //获取完整的年份(4位,1970-???? ...
- C++中的垃圾回收和内存管理(续)
boost memory的gc_allocator的使用 首先编译生成boost-memory的库,由于生成的是.so的动态库,所以需要在运行程序之前,将库文件的路径添加到LD_LIBRARY_PAT ...
- bzoj1008 矩乘递推
2013-11-17 10:38 原题传送门http://www.lydsy.com/JudgeOnline/problem.php?id=1008 比较水的题,直接矩阵乘法+递推就OK了 w[i,0 ...
- myeclipse打断点进入后无法查看变量的值的解决方法
myeclipse打断点进入后无法查看变量的值,打开mycelipse菜单选项:“Window” - “Preferences” - “Java” - “Editor” - “Hovers" ...
- quote(),unquote(),urlencode()编码解码
quote(),unquote(),quote_plus(),unquote_plus(),urlencode() ,pathname2url(),url2pathname() urllib中还提供了 ...
- IOC(控制反转)的理解
1.IOC的理论背景 我们知道在面向对象设计的软件系统中,它的底层都是由N个对象构成的,各个对象之间通过相互合作,最终实现系统地业务逻辑[1]. 图1 软件系统中耦合的对象 如果我们打开机械式手表的后 ...
- sybase ase 重启
sybase ase 重启 https://blog.csdn.net/davidmeng10/article/details/50344305 https://blog.csdn.net/wengy ...