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的更多相关文章

  1. Java学习笔记4

    Java学习笔记4 1. JDK.JRE和JVM分别是什么,区别是什么? 答: ①.JDK 是整个Java的核心,包括了Java运行环境.Java工具和Java基础类库. ②.JRE(Java Run ...

  2. 《Java学习笔记(第8版)》学习指导

    <Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...

  3. 20145330第五周《Java学习笔记》

    20145330第五周<Java学习笔记> 这一周又是紧张的一周. 语法与继承架构 Java中所有错误都会打包为对象可以尝试try.catch代表错误的对象后做一些处理. 使用try.ca ...

  4. java学习笔记11--集合总结

    java学习笔记系列: java学习笔记10--泛型总结 java学习笔记9--内部类总结 java学习笔记8--接口总结 java学习笔记7--抽象类与抽象方法 java学习笔记6--类的继承.Ob ...

  5. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  6. 0035 Java学习笔记-注解

    什么是注解 注解可以看作类的第6大要素(成员变量.构造器.方法.代码块.内部类) 注解有点像修饰符,可以修饰一些程序要素:类.接口.变量.方法.局部变量等等 注解要和对应的配套工具(APT:Annot ...

  7. Java学习笔记(04)

    Java学习笔记(04) 如有不对或不足的地方,请给出建议,谢谢! 一.对象 面向对象的核心:找合适的对象做合适的事情 面向对象的编程思想:尽可能的用计算机语言来描述现实生活中的事物 面向对象:侧重于 ...

  8. 0032 Java学习笔记-类加载机制-初步

    JVM虚拟机 Java虚拟机有自己完善的硬件架构(处理器.堆栈.寄存器等)和指令系统 Java虚拟机是一种能运行Java bytecode的虚拟机 JVM并非专属于Java语言,只要生成的编译文件能匹 ...

  9. 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用

    垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...

  10. 0028 Java学习笔记-面向对象-Lambda表达式

    匿名内部类与Lambda表达式示例 下面代码来源于:0027 Java学习笔记-面向对象-(非静态.静态.局部.匿名)内部类 package testpack; public class Test1{ ...

随机推荐

  1. Educational Codeforces Round 55 (Rated for Div. 2):E. Increasing Frequency

    E. Increasing Frequency 题目链接:https://codeforces.com/contest/1082/problem/E 题意: 给出n个数以及一个c,现在可以对一个区间上 ...

  2. hbase集群写不进去数据的问题追踪过程

    hbase从集群中有8台regionserver服务器,已稳定运行了5个多月,8月15号,发现集群中4个datanode进程死了,经查原因是内存 outofMemory了(因为这几台机器上部署了spa ...

  3. 对Office文档进行授权

    Microsoft.Office.Interop.Word.ApplicationClass app = new Microsoft.Office.Interop.Word.ApplicationCl ...

  4. python 写 excel 模块 : xlwt

    主要来自:[ python中使用xlrd.xlwt操作excel表格详解 ] 为了方便阅读, 我将原文两个模块拆分为两篇博文: [ python 读 excel 模块: xlrd ] [ python ...

  5. 鼠标DPI和液晶显示器分辨率的关系

    鼠标DPI和液晶显示器分辨率的关系 说起鼠标,有两个性能指标是我们不能忽略的.一是游戏玩家相当熟悉的扫描率(单位:Frames Per Second),二是我们今天要和大家探讨的鼠标的分辨率(单位:D ...

  6. MongoDB安装成为Windows服务

    使用以下命令将MongoDB安装成为Windows服务.笔者的MongoDB目录为C:\Program Files\MongoDB\Server\3.6\bin 切换到C:\Program Files ...

  7. 字符设备驱动ioctl实现用户层内核层通信

    测试代码实现 memdev.h #ifndef _MEMDEV_H_ #define _MEMDEV_H_ #include<linux/ioctl.h> #ifndef MEMDEV_M ...

  8. javascript中a标签把href属性设置为“javascript:void(0)”还是会打开空白页面的问题

    在项目中有个位置的点击a标签这里要加一个权限判断,但是之前使用的是js动态添加a标签,href的属性值是一个url,但是我要做权限判断之后,我的url就不能设置在href属性中了,这样的话我可以在a标 ...

  9. 使用DRF视图集时自定义action方法

    在我们用DRF视图集完成了查找全部部门,创建一个新的部门,查找一个部门,修改一个部门,删除一个部门的功能后,views.py的代码是这样子的: class DepartmentViewSet(Mode ...

  10. python类的__new__和__init__

    python的类,和其他语言有一点不太一样,就是,他把新建一个类和初始化一个类,分成了两个方法: __new__ __init__ 当然,想想就知道,肯定是__new__先发生,然后才是__init_ ...