【问题标题】:Why the treeset can't find this element?为什么树集找不到这个元素?
【发布时间】:2014-01-09 09:10:08
【问题描述】:

我有一个 Treeset,其中人们按他们的钱排序,但平等是基于名称的。 我有同名“jackie”的 jack 和 jackie,他们被认为是平等的。 jack 添加到树集中,jackie 没有。 contains() 上的 javadoc 说:

如果此集合包含指定元素,则返回 true。更多的 形式上,当且仅当此集合包含元素 e 时才返回 true 这样 (o==null ? e==null : o.equals(e))。

不幸的是行

System.out.println(peoples.contains(jackie));

jackie.equals(jack) 返回真时返回假。为什么?

这是完整的代码。

public class UsingSet {


 public static void main(String[] args) {


         People jo = new People("Jo");      
         People jack = new People("Jackie");
         jack.setMoney(12);
         People jim = new People("Jimmy");
         jim.setMoney(150);
         People john = new People("John");

         TreeSet<People> peoples = new TreeSet<People>();
         peoples.add(jo);
         peoples.add(jack);
         peoples.add(jim);
         peoples.add(john);


        People jackie = new People("Jackie");
        System.out.println("equality ? "+(jackie.equals(jack)));
        System.out.println(peoples.contains(jackie));

    }
}

class People extends Object implements Comparable<People> {

    public static long maxCount() {
        return 25000000000L;
    }

    String name;
    Float  money = 1000f;

    public People(String name) {
        super();
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.length());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        People other = (People) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return name;
    }

    @Override
    public int compareTo(People other) {

        int result = this.money.compareTo(other.getMoney());
        if (result == 0){
            //finding a second criteria
            return this.name.compareTo(other.getName());        
        }else{
            return result;
        }
    }


    public float getMoney() {
        return money;
    }

    public void setMoney(float money) {
        this.money = money;
    }

}

编辑: javadoc 说基于 自然顺序 的 Treeset 必须具有与 compareTo() 一致的 equals()。带有Comparator 的 Treeset 一定不能。

所以我像这样稍微修改了代码:

Comparator<People> compareByMoney = new Comparator<People>() {

  @Override
  public int compare(People p1, People p2) {
    int result = p1.money.compareTo(p2.getMoney());
    if (result == 0){
    //finding a second criteria
    return p1.name.compareTo(p2.getName());     
    }else{
      return result;
    }           
}

};      

TreeSet<People> peoples = new TreeSet<People>(compareByMoney);
    ...
    System.out.println(peoples.contains(jackie)); //--> true

【问题讨论】:

  • 仅供参考,对我来说是正确的。 ideone.com/anwxoX
  • 我更正了 main 的最后一行:我测试 System.out.println(peoples.contains(jackie));这是错误的

标签: java collections


【解决方案1】:

jackie 不是正式的equals,因为他们没有相同数量的钱(请注意,您的 equals 方法只检查名称,而不是钱)。

您正在使用 money 属性在树集中比较它们。由于 jackie 有 1000 个钱,而 jack 有 12 个,它们对于树集并不相同,因此包含 return false..

如果你这样做了

People jackie = new People("Jackie");
jackie.setMoney(12);

你会发现它对两者都输出 true,或者如果你的 compareTo 方法只是:

    @Override
    public int compareTo(People other) {
       return this.name.compareTo(other.getName());     
    }

它还会输出true

因此您需要更改您的equals 方法来比较money 的数量,或者只使用您的compareTo 方法中的名称。

如果您阅读文档:

请注意,由集合维护的顺序(无论是否显式 提供了比较器)必须与equals一致如果要 正确实现 Set 接口。 (见可比较或比较 以获得与等于一致的精确定义。) 之所以如此,是因为 Set 接口是根据等式定义的 操作,但 TreeSet 实例执行所有元素比较 使用它的 compareTo(或比较)方法,所以两个元素是 这种方法认为相等,从集合的角度来看,相等

一致:

C 类的自然排序据说是与 等于 当且仅当 e1.compareTo(e2) == 0 具有相同的布尔值 为 C 类的每个 e1 和 e2 的 e1.equals(e2)

我们有你的代码:

System.out.println("equality ? "+(jackie.equals(jack))); //equality ? true
System.out.println(jackie.compareTo(jack)); //1

你的类的自然顺序与 equals 不一致。所以不要指望你的 treeSet 有正常的行为。

【讨论】:

  • 是的,我的 CompareTo 与 equals() 无关,我不想更改它。但是javadoc明确表示包含基于equals()。我还可以对 Treeset 使用 Comparator 并删除人的 Comparable 接口。
  • @NicolasZozol 您的 equals 方法与您的自然比较器不一致。检查我的编辑。
  • 很好的答案:) 必须与equals一致是问题所在。所以意味着如果compareTo() == 0,那么equals() 应该返回true。但这意味着 Treeset 非常有限。
  • @NicolasZozol 你到底想达到什么目的?
  • 我正在进行 Java 培训,所以主要是理论。我添加了一个编辑来找到一个好的解决方案:人仍然与名字相等,但是树集使用比较器将人与他们的钱进行比较。
【解决方案2】:

我只是运行你的代码,它 println "true"

【讨论】:

  • 不是回答。将其添加到评论部分。
  • 我没有添加评论的权限。
【解决方案3】:

请看懂二叉树拳头才知道原因

compareTo() 方法用于在树中插入任何元素(不是 equals() 方法)。 杰克和杰基有不同数量的钱。所以根据 compartTo() 方法两者都不是同一个对象。并且由于 compareTo() 方法返回 false。这就是为什么 contains() 方法也返回 false

    People jackie = new People("Jackie");
    System.out.println("equality ? "+(jackie.equals(jack)));
    System.out.println("compareTo ? "+(jackie.compareTo(jack)));
    System.out.println(peoples.contains(jackie));

如果您的 equals() 方法返回 true,那么您的 compareTo() 方法也应该显示相同的行为。

【讨论】:

    【解决方案4】:

    我认为您没有注意到您的算法不适合您的目的。

    好的,您将 jackjackie 添加到 TreeSet。 杰克得到了name ="jackie"money="12" Jackie 得到了name="jackie"money= &lt;defaultValue&gt; ="1000"

    当你在 People 类中重写 equals 方法时(主要是检查 2 个对象的名称),你通过这种方式得到你的检查 "equality ? "+(jackie.equals(jack)),这显然是正确的

    但是,当您在 People 类中重写方法 compareTo 时,您首先检查 2 个对象之间的货币,因此之前您声明 jackjackie 时没有相同的货币属性值。事实上,它们是不同的,因为您的支票 != 0 和您对姓名的第二张支票被忽略了。

    好吧,如果你改变: People jackie = new People("Jackie");People jackie = new People("Jackie"); jackie.setMoney(12);

    你启动它,魔法! 控制台输出:
    equality ? true
    true

    问题解决了,伙计

    【讨论】:

      猜你喜欢
      • 2015-08-08
      • 1970-01-01
      • 2011-10-14
      • 2020-05-26
      • 2020-09-14
      • 2015-01-28
      • 1970-01-01
      • 1970-01-01
      • 2015-11-19
      相关资源
      最近更新 更多