引言: 最近在处理一个问题,大致是这个样子,从数据库里面取出一个集合,取出来的数据放到一个JavaBean里面。结果得到的集合长度为1.

TreeSetSet的一个实现,默认实现排序;故TreeSet的泛型类型必须是Comparable或者Comparator。TreeSet基于TreeMap实现。

实例

public class Person implements Comparable<Person>{
private String name;
private int order;
public Person(String name , int order){
this.name = name;
this.order = order;
}
public static void main(String [] args){
TreeSet<Person> users = new TreeSet<Person>();
users.add(new Person("Jason Kidd",1));
users.add(new Person("Kobe Bryant",1));
users.add(new Person("Jasme Harden",1));
users.add(new Person("Leborn James",1));
System.out.println(" size= "users.size());
}
// getter and setter
}
size= 1

认知被改写

上面代码的结果应该是4,因为我们没有重写hashCode和equals方法啊,Set的去重逻辑变了吗?

重写上面的例子:

public class Person implements Comparable<Person>{
public static void main(String [] args){
TreeSet<Person> users = new TreeSet<Person>();
users.add(new Person("Jason Kidd",1));
users.add(new Person("Kobe Bryant",2));
users.add(new Person("Jasme Harden",1));
users.add(new Person("Leborn James",1));
System.out.println(" size= "users.size());
}
// getter and setter
}
size= 2

表面看好像是compareTo方法觉得了去重;为了证实这个猜测,进入下面的代码里面去证实下吧。

源码分析

理论依据

 * <p>Note that the ordering maintained by a set (whether or not an explicit
* comparator is provided) must be <i>consistent with equals</i> if it is to
* correctly implement the {@code Set} interface. (See {@code Comparable}
* or {@code Comparator} for a precise definition of <i>consistent with
* equals</i>.) This is so because the {@code Set} interface is defined in
* terms of the {@code equals} operation, but a {@code TreeSet} instance
* performs all element comparisons using its {@code compareTo} (or
* {@code compare}) method, so two elements that are deemed equal by this method
* are, from the standpoint of the set, equal. The behavior of a set
* <i>is</i> well-defined even if its ordering is inconsistent with equals; it
* just fails to obey the general contract of the {@code Set} interface.
//翻译
注意,如果要正确实现 Set 接口,则 set 维护的顺序(无论是否提供了显式比较器)必须与 equals 一致。
(关于与 equals 一致 的精确定义,请参阅 Comparable 或 Comparator。)这是因为 Set 接口是按照
equals 操作定义的,但 TreeSet 实例使用它的 compareTo(或 compare)方法对所有元素进行比较
,因此从 set 的观点来看,此方法认为相等的两个元素就是相等的。即使 set 的顺序与 equals 不一致,
其行为也是 定义良好的;它只是违背了 Set 接口的常规协定。

上面提到了TreeSet的equal方法是依据Comparable或者Comparator接口判断的,和前面测试的结果一致。

源码解读:

代码流转:

前面提过了,TreeSet是底层是基于TreeMap存储数据的:

public class TreeSet{
public TreeSet() {
this(new TreeMap<E,Object>());
}
}

构造TreeSet的时候,并没有检查参数类型必须是Comparable或者Comparator的实现类。

public class TreeSet{
private static final Object PRESENT = new Object();
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
}

添加元素的操作,实际就是如何给TreeMap增加元素的操作。

// java.util.TreeMap.java
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 = 1;
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);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
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<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
final int compare(Object k1, Object k2) {
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}

上面一段看起来很长,我们重看其中几句就好了

  • TreeMap内置默认的Comparator比较器,一旦设定,后续所有的比较必须依据此比较器进行;发现不符合比较器方式的,抛出异常。
  • 核心的两个do-while循环,依据比较器结果变量__cmp__,判断是将值插入的觉提位置。一旦__cmp=0__将value放到相同位置。

相同的key会覆盖,所有前面测试的时候,集合长度不增加,主要因为compareTo方法返回了0.

Java解惑之TreeSet是如何去重的的更多相关文章

  1. Java中5种List的去重方法及它们的效率对比,你用对了吗?

    01.使用两个for循环实现List去重(有序) /**使用两个for循环实现List去重(有序)     *     * @param list     * */    public static  ...

  2. Java解惑五:类之谜

    本文是依据JAVA解惑这本书,做的笔记.电子书见:http://download.csdn.net/detail/u010378705/7527721 谜题46 函数重载的问题. JAVA重载解析过程 ...

  3. 【Java解惑】表达式问题

    1. 如果判断一个参数是否是奇数? 我们通过下面代码来尝试一下,看看方法可行不: public static boolean isOdd(int i) { return i % 2 == 1; } p ...

  4. Java深入了解TreeSet

    Java中的TreeSet是Set的一个子类,TreeSet集合是用来对象元素进行排序的,同样他也可以保证元素的唯一.那TreeSet为什么能保证元素唯一,它是怎样排序的呢?先看一段代码: publi ...

  5. Java解惑八:很多其它库之谜

    本文是依据JAVA解惑这本书,做的笔记. 电子书见:http://download.csdn.net/detail/u010378705/7527721 谜题76 将线程的启动方法start(),写成 ...

  6. Java HashSet对txt文本内容去重(统计小说用过的字或字数)

    Java HashSet对txt文本内容去重(统计小说用过的字或字数) 基本思路: 1.字节流读需要去重的txt文本.(展示demo为当前workspace下名为utf-8.txt的文本) 2.对读取 ...

  7. java解惑之常常忘记的事

    java解惑之常常忘记的事 2012-10-17 18:38:57|  分类: JAVA |  标签:基础知识  软件开发  |举报|字号 订阅     针对刚接触java的菜鸟来说,java基础知识 ...

  8. Java HashSet和TreeSet【笔记】

    Java HashSet和TreeSet[笔记] PS:HashSet.TreeSet 两个类是在 Map 的基础上组装起来的类 HashSet 类注释 1.底层实现基于 HashMap,所以迭代时不 ...

  9. JAVA中的TreeSet

    TreeSet简介 TreeSet是一个有序的集合,它的作用是提供一个有序的Set集合,它继承于AbstractSet抽象类实现了NavigableSet<E>, Cloneable, j ...

随机推荐

  1. The number of sections contained in the collection view after the update (1) must be equal to the number of sections contained in the collection view before the update (0), plus or minus the number of

    现象:当删除CollectionView 当中的某个section的时候,报上面的错误 初步分析:当前CollectionView删除前后都不止一个Section,怎么会报那样的错误:猜想可能是相册界 ...

  2. iOS原生分享功能

    iOS_系统原生分享 - CSDN博客 通过UIActivityViewController实现更多分享服务 - 简书 UIActivity - UIKit _ Apple Developer Doc ...

  3. 用js计算自己从出生到现在生活了多长时间(x天零x小时零x分钟零x秒) 初学者,大家多多包涵,有不足的地方请多包涵。

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. MySQL->索引的维护[20180504]

    学习MySQL数据库中表的索引维护(新增和删除)       索引的好处:             提高查询的效率             可限定特定的资料(如唯一)     索引的不足:       ...

  5. putty登录出现access denied的解决办法

    [转]https://www.aliyun.com/jiaocheng/152659.html 在/etc/ssh/sshd_config 中有个 PermitRootLogin, 改成“Permit ...

  6. jQuery增减类操作代码

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  7. 一位老手关于HTML5的见解

    HTML5新特性总结  HTML5属于上一代HTML的新迭代语言,设计HTML5最主要的目的是为了在移动设备上支持多媒体!!!例如: video 标签和 audio 及 canvas 标记   HTM ...

  8. MySQL的主从复制+双主模式

    MySQL的主从复制 部署环境: MySQL master 192.168.40.21 MySQL slave  192.168.40.22 思路: 当主MySQL上进行数据上的操作或者变化时,主My ...

  9. 加法变乘法——第六届蓝桥杯C语言B组(省赛)第六题

    原创 加法变乘法 我们都知道:1+2+3+ ... + 49 = 1225现在要求你把其中两个不相邻的加号变成乘号,使得结果为2015 比如:1+2+3+...+10*11+12+...+27*28+ ...

  10. Using Angular 1.x With ES6 and Webpack

    http://angular-tips.com/blog/2015/06/using-angular-1-dot-x-with-es6-and-webpack/