今天在网上看到一个问题:一个已经构建好的 TreeSet,怎么完成倒排序?

网上给出的答案是:

通过TreeSet构造函数传入一个比较器,指定比较器进行排序为原排序的倒叙。
TreeSet的自然排序是根据集合元素的大小,TreeSet将他们以升序排列。如果需要实现定制排序,例如降序,则可以使用Comparator接口。该接口里包含一个int compare(T o1,T o2)方法,该方法用于比较o1和o2的大小。
如果需要实现定制排序,则需要在创建TreeSet集合对象时,并提供一个Comparator对象与该TreeSet集合关联,由该Comparator对象负责集合元素的排序逻辑。

我们知道,如果要实现TreeSet 的 排序(或者说让一个TreeSet可用),必须让加入的对象具有可排序性,否则就会报错 java.lang.ClassCastException。

实现思路有两个(二选一即可):

1、加入的对象(相对于TreeMap,就是key对象,TreeSet 是通过 TreeMap 来实现的)需要实现 Comparable 接口

package com.cd.demo;

import java.util.Set;
import java.util.TreeSet; public class TreeSetTest1 { public static void main(String[] args) {
// 构造一个可用的TreeSet对象:传入对象实现 Comparable 接口 (按照年龄排序)
Set<User> userSet = new TreeSet<>();
userSet.add(new User(5, 32, "阿布1"));
userSet.add(new User(12, 25, "阿布2"));
userSet.add(new User(9, 36, "阿布3"));
userSet.add(new User(20, 34, "阿布4"));
userSet.add(new User(15, 48, "阿布5"));
for (User u : userSet) {
System.out.println(u.toString());
}
} // static 内部类,可以在main方法中 直接使用new User(...)初始化,否则就要:new TreeSetTest().new User(...) 才行
static class User implements Comparable<User> {
Integer id; Integer age; String name; public User(Integer id, int age, String name) {
super();
this.id = id;
this.age = age;
this.name = name;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public Integer getAge() {
return age;
} public void setAge(Integer age) {
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "User [id=" + id + ", age=" + age + ", name=" + name + "]";
} @Override
public int compareTo(User arg0) {
return this.age.compareTo(arg0.getAge());
}
} }
User [id=12, age=25, name=阿布2]
User [id=5, age=32, name=阿布1]
User [id=20, age=34, name=阿布4]
User [id=9, age=36, name=阿布3]
User [id=15, age=48, name=阿布5]

2、TreeSet初始化时需要一个 Comparator 接口实例 的入参

package com.cd.demo;

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet; public class TreeSetTest2 { public static void main(String[] args) {
// 构造一个可用的TreeSet对象:传入一个Comparator 接口实例作为入参 (按照id排序)
Set<User> userSet = new TreeSet<>(new Comparator<User>() {
@Override
public int compare(User arg0, User arg1) {
return arg0.getId().compareTo(arg1.getId());
}
});
// 因为 userSet 已经有入参 Comparator,故不会报错
userSet.add(new User(5, 32, "阿布1"));
userSet.add(new User(12, 25, "阿布2"));
userSet.add(new User(9, 36, "阿布3"));
userSet.add(new User(20, 34, "阿布4"));
userSet.add(new User(15, 48, "阿布5"));
for (User u : userSet) {
System.out.println(u.toString());
}
} static class User {
Integer id; Integer age; String name; public User(Integer id, int age, String name) {
super();
this.id = id;
this.age = age;
this.name = name;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public Integer getAge() {
return age;
} public void setAge(Integer age) {
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "User [id=" + id + ", age=" + age + ", name=" + name + "]";
} } }
User [id=5, age=32, name=阿布1]
User [id=9, age=36, name=阿布3]
User [id=12, age=25, name=阿布2]
User [id=15, age=48, name=阿布5]
User [id=20, age=34, name=阿布4]

 那么,现在问题来了,如果TreeSet中 保存的对象实现了 Comparable 接口,而 TreeSet 又传入了外部的 Comparator,会怎么样呢,会出现排序冲突吗?

看代码:

package com.cd.demo;

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet; public class TreeSetTest { public static void main(String[] args) {
// 构造一个可用的TreeSet对象:要么传入一个Comparator 接口实例作为入参,要么 key 必须实现 Comparable 接口
// 现在同时两个都满足,User内部按照 age 排序,外部 Comparator 按照id 排序,会怎么样呢
Set<User> userSet = new TreeSet<>(new Comparator<User>() {
@Override
public int compare(User arg0, User arg1) {
return arg0.getId().compareTo(arg1.getId());
}
});
// 因为 userSet 已经有入参 Comparator,故不会报错
userSet.add(new User(5, 32, "阿布1"));
userSet.add(new User(12, 25, "阿布2"));
userSet.add(new User(9, 36, "阿布3"));
userSet.add(new User(20, 34, "阿布4"));
userSet.add(new User(15, 48, "阿布5"));
// userSet 构造测试(同时实现内外排序时,按照 Comparator 外部排序来)
for (User u : userSet) {
System.out.println(u.toString());
}
} // static 内部类,可以在main方法中 直接使用new User(...)初始化,否则就要:new TreeSetTest().new User(...) 才行
static class User implements Comparable<User> {
Integer id; Integer age; String name; public User(Integer id, int age, String name) {
super();
this.id = id;
this.age = age;
this.name = name;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public Integer getAge() {
return age;
} public void setAge(Integer age) {
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "User [id=" + id + ", age=" + age + ", name=" + name + "]";
} @Override
public int compareTo(User arg0) {
return this.age.compareTo(arg0.getAge());
}
} }
User [id=5, age=32, name=阿布1]
User [id=9, age=36, name=阿布3]
User [id=12, age=25, name=阿布2]
User [id=15, age=48, name=阿布5]
User [id=20, age=34, name=阿布4]

  可以看出,如果同时实现了内外排序(对象实现 Comparable 接口、TreeSet 初始化 传入 Comparator 接口对象),则排序 以 Comparator 的外部排序进行

看一下TreeSet 的 add 方法源码,内部其实调用的是TreeMap 的 put:

public V put(K key, V value) {
// 根节点
Entry<K,V> t = root;
// 如果根节点为空,则直接创建一个根节点,返回
if (t == null) {
// TBD:
// 5045147: (coll) Adding null to an empty TreeSet should
// throw NullPointerException
//
// compare(key, key); // type check
root = new Entry<K,V>(key, value, null);
size = 1;
modCount++;
return null;
}
// 记录比较结果
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
// 当前使用的比较器
Comparator<? super K> cpr = comparator ;
// 如果比较器不为空,就是用指定的比较器来维护TreeMap的元素顺序
if (cpr != null) {
// do while循环,查找key要插入的位置(也就是新节点的父节点是谁)
do {
// 记录上次循环的节点t
parent = t;
// 比较当前节点的key和新插入的key的大小
cmp = cpr.compare(key, t. key);
// 新插入的key小的话,则以当前节点的左孩子节点为新的比较节点
if (cmp < 0)
t = t. left;
// 新插入的key大的话,则以当前节点的右孩子节点为新的比较节点
else if (cmp > 0)
t = t. right;
else
// 如果当前节点的key和新插入的key想的的话,则覆盖map的value,返回
return t.setValue(value);
// 只有当t为null,也就是没有要比较节点的时候,代表已经找到新节点要插入的位置
} while (t != null);
}
else {
// 如果比较器为空,则使用key作为比较器进行比较
// 这里要求key不能为空,并且必须实现Comparable接口
if (key == null)
throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;
// 和上面一样,喜欢查找新节点要插入的位置
do {
parent = t;
cmp = k.compareTo(t. key);
if (cmp < 0)
t = t. left;
else if (cmp > 0)
t = t. right;
else
return t.setValue(value);
} while (t != null);
}
// 找到新节点的父节点后,创建节点对象
Entry<K,V> e = new Entry<K,V>(key, value, parent);
// 如果新节点key的值小于父节点key的值,则插在父节点的左侧
if (cmp < 0)
parent. left = e;
// 如果新节点key的值大于父节点key的值,则插在父节点的右侧
else
parent. right = e;
// 插入新的节点后,为了保持红黑树平衡,对红黑树进行调整
fixAfterInsertion(e);
// map元素个数+1
size++;
modCount++;
return null;
}

注意看上面的下划线部分代码,jdk 中 默认是先使用 Comparator ,如果没有 Comparator ,才使用对象的 Comparable 接口。

那回到开篇的问题,如果需要实现倒序,就得分两种情况了:

1、只实现了 Comparable 的倒序;

2、实现了 Comparator 的倒序。

这两种情况,都可以通过 重新 建立一个 TreeSet,传入倒序的 Comparator 来实现倒序,伪代码为:

        Set<User> userSetNew = new TreeSet<>(new Comparator<User>() {
@Override
public int compare(User arg0, User arg1) {
return ...; // 新的排序逻辑
}
});
userSetNew.addAll(userSetOld);
System.out.println("--------------- 倒序后 ----------------");
for (User u : userSetNew ) {
System.out.println(u.toString());
}

这个思路有一种投机取巧的方法,就是 compare 方法中默认返回 -1,比如上面的代码实现倒序可以为:

package com.cd.demo;

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet; public class TreeSetTest { public static void main(String[] args) {
// 构造一个可用的TreeSet对象:要么传入一个Comparator 接口实例作为入参,要么 key 必须实现 Comparable 接口
// 现在同时两个都满足,User内部按照 age 排序,外部 Comparator 按照id 排序,会怎么样呢
Set<User> userSet = new TreeSet<>(new Comparator<User>() {
@Override
public int compare(User arg0, User arg1) {
return arg0.getId().compareTo(arg1.getId());
}
});
// 因为 userSet 已经有入参 Comparator,故不会报错
userSet.add(new User(5, 32, "阿布1"));
userSet.add(new User(12, 25, "阿布2"));
userSet.add(new User(9, 36, "阿布3"));
userSet.add(new User(20, 34, "阿布4"));
userSet.add(new User(15, 48, "阿布5"));
// userSet 构造测试(同时实现内外排序时,按照 Comparator 外部排序来)
for (User u : userSet) {
System.out.println(u.toString());
} Set<User> userSet2 = new TreeSet<>(new Comparator<User>() {
@Override
public int compare(User arg0, User arg1) {
return -1;
}
});
userSet2.addAll(userSet);
System.out.println("--------------- 倒序后 ----------------");
for (User u : userSet2) {
System.out.println(u.toString());
}
} // static 内部类,可以在main方法中 直接使用new User(...)初始化,否则就要:new TreeSetTest().new User(...) 才行
static class User implements Comparable<User> {
Integer id; Integer age; String name; public User(Integer id, int age, String name) {
super();
this.id = id;
this.age = age;
this.name = name;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public Integer getAge() {
return age;
} public void setAge(Integer age) {
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "User [id=" + id + ", age=" + age + ", name=" + name + "]";
} @Override
public int compareTo(User arg0) {
return this.age.compareTo(arg0.getAge());
}
} }
User [id=5, age=32, name=阿布1]
User [id=9, age=36, name=阿布3]
User [id=12, age=25, name=阿布2]
User [id=15, age=48, name=阿布5]
User [id=20, age=34, name=阿布4]
--------------- 倒序后 ----------------
User [id=20, age=34, name=阿布4]
User [id=15, age=48, name=阿布5]
User [id=12, age=25, name=阿布2]
User [id=9, age=36, name=阿布3]
User [id=5, age=32, name=阿布1]

  

TreeMap 的排序冲突吗的更多相关文章

  1. TreeMap中文排序,TreeMap倒序输出排列

    1.TreeMap集合倒序排列 import java.util.Comparator; /** * 比较算法的类,比较器 * @author Administrator * */ public cl ...

  2. TreeMap定制排序和自然排序

    TreeMap定制排序和自然排序自然排序是实现Comparable接口的方法.代码如下: @Override public int compareTo(Object o) { if (o instan ...

  3. Java TreeMap的排序

    TreeMap 和 HashMap 用法大致相同,但实际需求中,我们需要把一些数据进行排序:以前在项目中,从数据库查询出来的数据放在List中,顺序都还是对的,但放在HashMap中,顺序就完全乱了. ...

  4. treeMap,key排序,value排序

    HashMap与TreeMap按照key和value排序 使用一个场景是mapreduce中用解决topn问题是用value 排序 topn MapReducetopN

  5. HashMap , TreeMap , TreeMap 默认排序

    Java代码  import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java. ...

  6. TreeMap 排序

    一.TreeMap TreeMap 默认排序规则:按照key的字典顺序来排序(升序) 当然,也可以自定义排序规则:要实现Comparator接口. 用法简单,先看下下面的demo public cla ...

  7. Treemap 有序的hashmap。用于排序

    TreeMap:有固定顺序的hashmap.在需要排序的Map时候才用TreeMap. Map.在数组中我们是通过数组下标来对其内容索引的,键值对. HashMap HashMap 用哈希码快速定位一 ...

  8. TreeMap 底层是红黑树 排序是根据key值进行的 添加元素时异常 Comparable异常 Comparator比较自定义对象放在键的位置

    package com.swift; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; ...

  9. Java基础操作面试题:Map集合排序 需要TreeMap 构造方法参数有比较器 输入字符串,统计A、B、C、D、出现次数,由高到低输出字母和出现次数,使用Map集合完成此题

    Map和Collections是同级别的,不能像List排序那样直接用Collections.sort(new Comparator<?>(){ 复写compara方法}); HashMa ...

随机推荐

  1. Vue(小案例_vue+axios仿手机app)_图文列表实现

    一.前言 1.导航滑动实现   2.渲染列表信息时让一开始加载的时候就请求数据 3.根据路由的改变,加载图文的改变(实现用户访问网站时可能执行的三个操作) 二.主要内容 1.导航滑动实现: (1)演示 ...

  2. 应用调试(四)系统调用SWI

    目录 应用调试(四)系统调用SWI 系统调用 SWI代码片段分析 分析sys_write 构造sys_hello 应用程序调用SWI 嵌入汇编语法 测试APP 参考 title: 应用调试(四)系统调 ...

  3. Django admin修改密码

    django的admin用户被我多动症一样的测试,给密码弄丢了,需要重置. 从数据库重置的可能性为0,因为django对于密码有保护策略.考虑从运行程序的地方进行重置: 1.在程序的文件夹下,执行这样 ...

  4. [物理学与PDEs]第2章习题13 将 $p$ - 方程组化为守恒律形式的一阶拟线性对称双曲组

    试引进新的未知函数, 将 $p$ - 方程组 $$\beex \bea \cfrac{\p \tau}{\p t}-\cfrac{\p u}{\p x}&=0,\\ \cfrac{\p u}{ ...

  5. 【Unity]】AR小工具-Vuforia

    很有意思的增强现实玩具,六分钟应用. https://www.youtube.com/watch?v=khavGQ7Dy3c

  6. java8 list统计(求和、最大、最小、平均)

    list.stream().mapToDouble(User::getHeight).sum()//和 list.stream().mapToDouble(User::getHeight).max() ...

  7. Mongodb注入

    0x01 Brief Description 作为nosql(not only sql)数据库的一种,mongodb很强大,很多企业也在用到.相对于sql数据库,nosql数据库有以下优点:简单便捷. ...

  8. luogu P5291 [十二省联考2019]希望

    luogu loj 无论最终结果将人类历史导向何处 \(\quad\)我们选择 \(\quad\quad\)\(\large{希望}\) 诶我跟你讲,这题超修咸的 下面称离连通块内每个点距离不超过\( ...

  9. MYSQL实战

    基础架构 更新操作 日志模块 redo log 和 binlog 两阶段提交: prepare commit 事务隔离 读未提交:别人改数据的事务尚未提交,我在我的事务中也能读到.读已提交:别人改数据 ...

  10. 人体姿势识别,Convolutional pose machines文献阅读笔记。

    开源实现 https://github.com/shihenw/convolutional-pose-machines-release(caffe版本) https://github.com/psyc ...