【问题标题】:How can I compare generic objects without knowing anything about them? [duplicate]如何在不了解它们的情况下比较通用对象? [复制]
【发布时间】:2018-02-15 08:19:26
【问题描述】:

我需要知道两个对象的参数值相同。

我不知道对象的类型。 它们的参数具有相同的值,但可能有不同的引用。

喜欢:

Person person = new Person("Alfred", "00001" ...);
Person person2 = new Person("Alfred", "00001" ...);
list.add(person, person2);

记住我不知道对象的类型,我不能修改对象。 可能是人,动物......或其他东西。

我需要知道当我迭代列表时,它是否有重复的对象。

for (Object item : list) {

}

泰。

【问题讨论】:

  • 比较:o1.equals(o2) - 没有重复:new HashSet(list).size() == list.size()。您必须在您的课程中实现equalshashCode(如果给出了Person,则可以)。
  • 对不起,我需要消除重复的对象。

标签: java


【解决方案1】:

我看到有两种方法,一种是首选的 Java 典型方法,使用 equals() 方法,另一种是脆弱的基于反射的方法。

使用 equals()

所有体面的 Java 类都应该实现 equals() 方法,用于比较两个对象在语义上是否具有相同的内容。我猜这个定义符合您的要求“参数中的相同值”(这不是您真正要求的,而是有充分的理由 - 见下文)。当然,这依赖于具有适当 equals() 实现的相关类,或者您能够向相关类添加一个。如果可能的话,去那条路。

使用反射

免责声明:尽可能避免这种方式。

Java 允许您查找给定类具有的字段,并为给定实例读出此类字段的值。所以你可以:

  • 找到第一个对象的类。
  • 与第二个对象的类进行比较,如果不同则返回 false。
  • 查找类的字段。
  • 遍历字段,获取两个对象的字段值。
  • 比较字段值(使用 equals() 方法?递归使用基于反射的比较?-您决定...)。

为什么类特定的 equals() 方法比盲目的字段值比较更好

并非所有字段都是平等的。通常,一个类的内部字段与您在要求属性等价时要比较的对象属性无关。例子:

  • 为了使某些计算更快,实例缓存了一些相关数据。从技术上讲,填充或清空缓存是一个不同的字段值,但没有语义意义。
  • 实例维护一些lastAccess 日期。从技术上讲,大多数时候你会得到不同的值,但这并不意味着某些相关的对象属性是不同的。

盲目比较对象字段也总是会落入比较这些字段的陷阱,尽管它们没有您所期望的语义。

只有类本身知道哪些字段可以比较有用,哪些不可以。这就是为什么每个类都应该有自己的equals() 方法,以及为什么应该使用它来比较内容。

【讨论】:

  • 我使用了反射,非常感谢。
  • @downvoter:为什么?
  • @AlberrB 您是否阅读了所有建议?它就在那里。免责声明:尽可能避免这种方式。如果这是家庭作业,那么反射的使用是完全错误的。它会出现在我的任何项目中。
  • 是的,如果对象具有相同的引用,您的问题会起作用,但事实并非如此,我需要知道它们是否与参数的值相等: List l1 = new ArrayList ();每个人 = 新人 (); per.setNam("s1"); per.setID ("1"); l1.add(每);个人 per2 = new Person (); per2.setNam ("p2"); per2.setID("1"); l1.add (per2); Set set = new HashSet (l1) 输出:[Person @ 7852e922, Person @ 4e25154f]
  • 如果它只在相同引用的情况下有效,那么你的equals和hashCode的实现是不正确的。使用工具生成这些方法并从中学习。
【解决方案2】:

可能性一:equals()

在最简单的情况下,equals() 已在您要比较的对象的类中实现,以便比较内部状态。在这种情况下,您只需致电a.equals(b)(假设a 不是null)就可以收工了。

可能性2:使用反射

如果你不能假设equals() 已经以适合你的方式实现,那么它会变得有点困难。您应该寻找提供 deepEquals() 操作的库,这些库将使用反射来查看底层并比较实际类,以及是否等于两个对象中的字段。

我建议使用 Apache Commons Lang 提供的 EqualsBuilder 类,因为我发现它工作得很好。

请注意,据我所知,自 Java 7 以来就存在的 Objects.deepEquals() 实际上并没有根据其文档进行深度比较,因为它将工作委托给第一个参数的 equals() 方法。

当然,你也可以自己实现一个deepEquals()操作,如果你有时间知道或者想学习反射API。

【讨论】:

    【解决方案3】:

    在 Java 8 中(需要覆盖 hashCode()equals(Object obj) 方法):

    cat c1=new cat("lolo", "black");
    cat c3=new cat("lolo", "black");
    List<Object> arrayList = new ArrayList<Object>();
    arrayList.add(c1);
    arrayList.add(c3);
    List<Object> deduped = arrayList.stream().distinct().collect(Collectors.toList());
    

    旧 Java 版本:

    如果你想消除重复的对象使用HashSet并且必须实现(覆盖)hashCode()方法:

    public class cat {
        String name;
        String color;
        public cat(String name, String color) {
            super();
            this.name = name;
            this.color = color;
        }
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((color == null) ? 0 : color.hashCode());
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            return result;
        }
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            cat other = (cat) obj;
            if (color == null) {
                if (other.color != null)
                    return false;
            } else if (!color.equals(other.color))
                return false;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            return true;
        }
    
        public static void main(String[] args) {
            cat c1=new cat("lolo", "black");
            cat c2=new cat("lolo2", "white");
            cat c3=new cat("lolo", "black");
    
            List<Object> arrayList = new ArrayList<Object>();
            arrayList.add(c1);
            arrayList.add(c3);
    
            Set<Object> uniqueElements = new HashSet<Object>(arrayList);
            arrayList.clear();
            arrayList.addAll(uniqueElements);
            System.out.println(arrayList.size());//will be 1
    
        }
    
    }
    

    如果您需要更多信息如何做好hashcode(),您可以阅读此答案:Best implementation for hashCode method

    如果所有对象类都实现(覆盖)equal() 方法,则可以使用它。

    示例:

    public class cat {
        String name;
        String color;
        public cat(String name, String color) {
            super();
            this.name = name;
            this.color = color;
        }
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            cat other = (cat) obj;
            if (color == null) {
                if (other.color != null)
                    return false;
            } else if (!color.equals(other.color))
                return false;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            return true;
        }
    public static void main(String[] args) {
            cat c1=new cat("lolo", "black");
            cat c2=new cat("lolo2", "white");
            cat c3=new cat("lolo", "black");
    
            Object o1=c1;
            Object o2=c2;
            Object o3=c3;
            System.out.println(o1.equals(o2));//false
            System.out.println(o1.equals(o3));//true
    
        }
    
    }
    

    【讨论】:

    • 重复的呢?这只是一个相当平庸的 equals 实现的示例。
    • @Christian 你可以比较他们重写 hashcode() 方法和你哈希集删除重复。
    • 我知道。我刚刚提到,这不是您答案的一部分。稍后您添加了 hashCode。好的。但实际上在答案中编写 hashCode 和 equals 方法是相当多余的。但我没有投反对票。我希望你也没有。
    【解决方案4】:

    只需为列表中可能的每个类实现一个适当的equalshashCode 方法。

    然后将列表转换为集合,比较这两个集合的大小,就知道该列表中是否有重复项。

    Set set = new HashSet(list);
    if(set.size() == list.size()) {
      System.out.println("No duplicates.");
    } else {
      System.out.println("Found duplicates.");
    }
    

    【讨论】:

    • 对不起,我需要消除重复的对象。
    • 如果对象类没有实现 hashCode() 将无法工作
    • 为什么答案被否决了?重复项在集合中被消除。在 cmets 中提到 hashCode 方法之前,我提到了它。答案完全正确。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-20
    • 1970-01-01
    • 1970-01-01
    • 2021-12-20
    • 1970-01-01
    • 1970-01-01
    • 2014-12-30
    相关资源
    最近更新 更多