如何比较通用对象而不了解它们?

时间:2018-02-15 08:19:26

标签: java

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

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

像:

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

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

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

for (Object item : list) {

}

4 个答案:

答案 0 :(得分:1)

在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

    }

}

答案 1 :(得分:1)

可能性1:equals()

在最简单的情况下,equals()已在要比较的对象的类中实现,以便比较内部状态。在这种情况下,您只需致电a.equals(b)(假设a不是null)并将其称为一天。

可能性2:使用反思

如果你不能认为equals()已经以适合你的方式实施,那就变得有点困难了。您应该查找提供deepEquals()操作的库,这些库将使用反射来查看引擎并比较实际的类,如果等于两个对象中的字段。

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

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

当然,如果您有时间并且知道或想要学习Reflection API,您也可以自己实施deepEquals()操作。

答案 2 :(得分:1)

我看到有两种方式,使用equals()方法的首选Java方式,以及基于反射的脆弱方式。

使用equals()

所有体面的Java类都应该实现equals()方法,用于比较两个对象在语义上具有相同的内容。我想这个定义符合你的要求&#34;他们的参数中的相同值&#34; (它不是你要求的字面意思,但有充分理由 - 见下文)。当然,这依赖于具有适当equals()实现的相关类,或者您可以将一个添加到相关类。如果可能,就这样走。

使用反射

免责声明:如果可能,请尽量避免使用。

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

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

为什么特定于类的equals()方法优于盲区值比较

并非所有字段都是相同的。通常,类具有内部字段,这些字段与您在要求属性等效时要比较的对象属性无关。例子:

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

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

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

答案 3 :(得分:0)

只需为列表中的每个类实施适当的equalshashCode方法。

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

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