java - TreeSet 存储重复的自定义对象

标签 java set comparable

您好,我可能监督了一些事情,但事情就这样了。
我有一个TreeSet<CustomObject>我不想在集合中出现重复项。我的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)调用compareTo方法来自 Comparable接口(interface)并进行比较。这里的问题是它实际上只检查我认为最后一个对象?在集合中。因此,会发生的情况是,仅存在一个唯一的 CustomObject 条目。

这是我使用调试器跟踪时的情况:

  1. productCatalog.contains(p)
  2. 调用 compareTo
  3. 调用 equals检查是否 ean.equals(that.ean)
  4. 一次返回 true,但每次返回 false。因为它只检查最后一个对象

我怎样才能让它不仅检查步骤 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 语句方法,因为它总是返回 ´1 or -1 in my special case. Now I simply iterate over the Set to correctly use the equals` 方法,因为 TreeSet 使用compareTo() 来检查 Set 中的每个元素。

Java 文档声明如下

Note that the ordering maintained by a set (whether or not an explicit comparator is provided) must be consistent with equals if it is to correctly implement the Set interface. (See Comparable or Comparator for a precise definition of consistent with equals.) This is so because the Set interface is defined in terms of the equals operation, but a TreeSet instance performs all element comparisons using its compareTo (or 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 is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Set interface.

最佳答案

主要问题:

如果 product 和 other.product 都为 null,

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

旧答案:

您的 equalscompareTo 实现不适合一起使用。

equals 适用于 ean,compareTo 适用于产品。仅当您隐含地假设相等 ean 意味着相等的乘积时,这才有效。如果在您的测试用例中情况并非如此,则结果将是错误的。

无论哪种情况,都不是好的实现,因为这可能导致 a < b, b < c 但 a “等于”c。

关于java - TreeSet 存储重复的自定义对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37810085/

相关文章:

java - CompareTo 的通用比较类型

java - CompareTo() 在客户端/服务器应用程序中给出相互冲突的结果?

java - 在 JAVA 中循环 SerenityRest 响应

java - Java 中的设置方法

java - 使用可比较或比较器接口(interface)使用字符串 s1 的顺序对字符串 s2 进行排序

java - 实现 Comparable、compareTo 名称冲突 : "have the same erasure, yet neither overrides the other"

java - 如何在 PostgreSQL 上使用 java.lang.NumberFormatException 执行 liquibase

java - 通过扫描仪将 txt 文件的 1 行分配给字符串

python - 如何使用 python 找到最常见的集合?

iphone - 在 Linux 上设置 iPhone sdk?