java TreeSet:比较和相等

kno*_*r_d 5 java sortedset treeset

我想要使​​用属性“sort_1”排序的对象列表。但是当我想删除时,我希望它使用属性“id”。以下代码表示问题。

package javaapplication1;

import java.util.TreeSet;

public class MyObj implements Comparable<MyObj> {
    public long sort_1;
    public long id;

    public MyObj(long sort, long id) {
        this.sort_1=sort;
        this.id=id;
    }

    @Override
    public int compareTo(MyObj other) {        
        int ret = Long.compare(sort_1, other.sort_1);               
        return ret;
    }    

    public String toString() {
        return id+":"+sort_1;
    }

    public static void main(String[] args) {
        TreeSet<MyObj> lst=new TreeSet<MyObj>();

                MyObj o1 = new MyObj(99,1);
        MyObj o2 = new MyObj(11,9);

        lst.add(o1);
        lst.add(o2);    

        System.out.println(lst);

                MyObj o3 = new MyObj(1234, 1);
                //remove myObje with id 1
                boolean remove=lst.remove(o3);

                System.out.println(lst);
    }

}
Run Code Online (Sandbox Code Playgroud)

这段代码的输出是:

[9:11, 1:99]
[9:11, 1:99]
Run Code Online (Sandbox Code Playgroud)

我需要对列表进行排序,因为我对列表做了很多添加。我不想明确使用任何“排序”方法。我有哪些选择?

编辑:

我的要求是:具有 'id' 作为唯一的对象,但可以有具有重复 'sort' 值的对象。

mhl*_*hlz 7

昨天偶然我也发现了这一点。这似乎是TreeMap实现的一个产物(TreeMap 使用 TreeSet 来存储其条目)。

TreeMap 使用二叉搜索树来存储键/值对,但它只使用给定的 Comparator(或者如果键类实现 Comparable 则使用比较函数)来检查相等性,正如您在这段代码摘录中看到的那样:

final Entry<K,V> getEntry(Object key) {
    // Offload comparator-based version for sake of performance
    if (comparator != null)
        return getEntryUsingComparator(key);
    if (key == null)
        throw new NullPointerException();
    @SuppressWarnings("unchecked")
        Comparable<? super K> k = (Comparable<? super K>) key;
    Entry<K,V> p = root;
    while (p != null) {
        int cmp = k.compareTo(p.key);
        if (cmp < 0)
            p = p.left;
        else if (cmp > 0)
            p = p.right;
        else
            return p;
    }
    return null;
}
Run Code Online (Sandbox Code Playgroud)

我几乎称这是一个(实际上无法修复的)错误,因为Comparable 接口的 JavaDoc明确表示使用compareTo 函数返回 0 并不一定意味着“相等”:

强烈建议但不严格要求 (x.compareTo(y)==0) == (x.equals(y))。

您将无法按照您想要的方式在 TreeSet 中存储内容。我建议使用普通的HashMapLinkedHashMap,然后在需要使用Collections.sort对输出进行排序时对输​​出进行排序。

除此之外,我总是觉得实现 Comparable 接口很奇怪。大多数事物并没有真正具有立即显而易见的“自然”顺序。有时这可能会导致奇怪的错误(就像这个!),所以我通常总是仅在需要时使用自定义比较器进行排序。Java 8 也让编写这些代码变得非常简单!


Mic*_*ock 1

我认为您遇到的问题是您正在实现Comparable,但您的实现似乎与 equals 不一致 - 并且您没有实现任何相等方法。那是:

当且仅当对于 C 类的每个 e1 和 e2 而言,e1.compareTo(e2) == 0 与 e1.equals(e2) 具有相同的布尔值时,C 类的自然排序才被认为与 equals 一致

在您的情况下,当您构建这三个对象时:

MyObj o1 = new MyObj(99,1);
MyObj o2 = new MyObj(11,9);
MyObj o3 = new MyObj(1234, 1);
Run Code Online (Sandbox Code Playgroud)

您将看到 o1.compareTo(o3) == -1,而 o1.equals(o3) == false。

但你似乎想要 o1.equals(o3) == true 。

另外,请注意,如果对象已存在于集合中,TreeSet.add() 将返回 false。该检查基于 equals() 方法。

要解决此问题,请重写 Object.equals() 和 Object.hashCode(),以便它们考虑 MyObj.id 字段,并在它们不相等时继续在compareTo() 方法中使用 sort_1 字段。

package javaapplication1;

import java.util.TreeSet;

public class MyObj implements Comparable<MyObj> {

    public long sort_1;
    public long id;

    public MyObj(long sort, long id) {
        this.sort_1 = sort;
        this.id = id;
    }

    @Override
    public int compareTo(MyObj other) {
        return (this.equals(other))? 0 : Long.compare(sort_1, other.sort_1);
    }

    @Override
    public boolean equals(Object obj) {
        MyObj other = (MyObj) obj;
        return this.id == other.id && this.sort_1 == other.sort_1;
    }

    @Override
    public int hashCode() {
        return (int) id;
    }


    public String toString() {
        return id + ":" + sort_1;
    }

    public static void main(String[] args) {
        TreeSet<MyObj> lst = new TreeSet<MyObj>();

        MyObj o1 = new MyObj(99L, 1L);
        MyObj o2 = new MyObj(11L, 9L);
        MyObj o3 = new MyObj(1234L, 1L);       
        MyObj o4 = new MyObj(1234L, 1L);   

        System.out.println( "Adding o1: " + lst.add(o1));
        System.out.println( "Adding o2: " + lst.add(o2));
        System.out.println( "Adding o3: " + lst.add(o3));
        System.out.println( "Adding o4: " + lst.add(o4));        

        System.out.println(lst);

        System.out.println("o1.compareTo(o3) : " + o1.compareTo(o3));
        System.out.println("o1.equals(o3) : " + o1.equals(o3));

        //remove myObje with id 1
        boolean remove = lst.remove(o3);

        System.out.println(lst);
    }

}
Run Code Online (Sandbox Code Playgroud)

输出:

Adding o1: true
Adding o2: true
Adding o3: true
Adding o4: false
[9:11, 1:99, 1:1234]
o1.compareTo(o3) : -1
o1.equals(o3) : false
[9:11, 1:99]
Run Code Online (Sandbox Code Playgroud)