【问题标题】:TreeSet storing duplicate custom objectsTreeSet 存储重复的自定义对象
【发布时间】:2016-06-14 10:59:24
【问题描述】:

您好,我可能监督了一些事情,但它就是这样。
我有一个TreeSet<CustomObject>,我不想在 Set 中有重复项。我的CustomObject 类看起来像这样。

class CustomObject implements Comparable<CustomObject> {

    String product;
    String ean;

    public CustomObject(String ean){
      this.ean = ean; 
      // product is getting set via setter and can be null
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        CustomObject that = (CustomObject) o;

        return ean.equals(that.ean);

    }

    @Override
    public int hashCode() {
        return ean.hashCode();
    }

    @Override
    public int compareTo(CustomObject another) {
        if(equals(another)) return 0;
        if(product != null && another.product == null) return -1;
        if(product == null) return 1;

        return product.compareToIgnoreCase(another.product);
    }
}

现在我有一个新对象的添加函数。

private final TreeSet<CustomObject> productCatalog;

public void addObject(SomeData tag) {
    CustomObject p = new CustomObject(tag.getEan());

    if (productCatalog.contains(p)) { // <-- This checks only one entry of the Set.
        for (CustomObject temp : productCatalog) {
            if (temp.equals(p)) {
                p = temp; // I do stuff with that later which is irrelevent here
            }
        }
    } else {
       productCatalog.add(p);
    }   
} 

方法productCatalog.contains(p)Comparable 接口调用compareTo 方法并进行比较。这里的问题是它实际上只检查我认为最后一个对象?在集合中。所以发生的情况是只存在一个唯一的 CustomObject 条目。

这是我使用调试器跟踪它时的场景:

  1. productCatalog.contains(p)
  2. 致电compareTo
  3. 调用equals检查ean.equals(that.ean)
  4. 一次返回真,但每隔一次返回假。因为它只检查最后一个对象

如何让它不仅检查第 4 步中的一个对象,而且检查集合中的所有当前对象。我错过了什么?

谢谢!


编辑:这些是一些示例数据。为简单起见,SomeData tag 基本上是一个字符串。

First run:

addObject("Ean1") // success added
addObject("Ean2") // success added
addObject("Ean3") // success added
addObject("Ean4") // success added

所有内容都被添加到 TreeSet 中。

Second run:
addObject("Ean1") // failed already in the map
addObject("Ean2") // failed already in the map
addObject("Ean3") // failed already in the map
addObject("Ean5") // success added
addObject("Ean4") // success added
addObject("Ean4") // success added

出于测试目的,我根据字符串 ean 手动设置产品名称。

public CustomObject(String ean){
      this.ean = ean; 
       switch(ean){
            case "Ean1": product = "TestProduct"; break;
            case "Ean2": product = "ProductTest";break;
            case "Ean3": product = "Product";break;
    }

TreeSet 充当缓存。


Edit2:我就是这样解决的。

            for (CustomObject temp : productCatalog) {
                if (temp.equals(p)) {
                    p = temp; // I do stuff with that later which is irrelevent here
                }
            }

我使用 contains 方法删除了 if 语句,因为 TreeSet 使用 compareTo() 来检查 Set 中的每个元素,所以它总是会返回 ´1or-1in my special case. Now I simply iterate over the Set to correctly use theequals` 方法。

Java Docs 声明如下

请注意,由集合维护的顺序(无论是否显式 提供了比较器)必须与equals一致,如果它是 正确实现 Set 接口。 (见可比较或比较 用于与等于一致的精确定义。)就是这样 因为 Set 接口是根据 equals 操作定义的, 但是 TreeSet 实例使用它的执行所有元素比较 compareTo(或比较)方法,因此两个元素被视为相等 通过这种方法,从集合的角度来看,它们是相等的。这 一个集合的行为是明确定义的,即使它的顺序是不一致的 等于;它只是不遵守集合的一般合同 界面。

【问题讨论】:

  • 您能否更具体地说明您遇到的错误行为类型?集合中实际上是否存在“双重元素”? “最后一个对象”是什么意思?

标签: java set comparable


【解决方案1】:

主要问题:

compareTo 如果 product 和 other.product 都为空,则返回 1。这是错误的,因为它们实际上是相等的。您可能忘记为较高的 ean 值设置产品名称,例如“Ean4”和“Ean5”。

旧答案:

equalscompareTo 的实现不适合。

equals 适用于 ean,compareTo 适用于产品。这仅在您隐含假设相等 ean 意味着相等产品时才有效。如果在您的测试用例中这不是真的,那么结果将是错误的。

在任何一种情况下,这都不是一个好的实现,因为这可能导致 a

【讨论】:

  • 感谢您的回答。要求基本是这样的。按产品名称排序的列表,如果没有可用的产品名称,则应将其附加到列表的末尾。
  • Ean4 和 Ean5 没有产品名称是故意的。这些应该附加到实际与 compareTo 一起使用的列表的末尾。问题在于equals 方法,因为它只检查集合中的一个对象。感谢您的努力。
  • 如果您在 TreeSet 中搜索元素,“equals”仅在 compareTo 计算结果为 0 时应用。如果 compareTo 计算结果为 1 或 -1,则假定元素不相等。因此,您的 compareTo 实现必须与您的 equals 实现兼容。
  • 这让我真正走上了正轨。我会尽快发布我的解决方案。谢谢!
猜你喜欢
  • 1970-01-01
  • 2015-11-26
  • 1970-01-01
  • 2011-02-28
  • 1970-01-01
  • 2013-03-02
  • 1970-01-01
  • 1970-01-01
  • 2020-07-07
相关资源
最近更新 更多