有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java ArrayList未使用重写的equals

我在获取ArrayList以正确使用重写的equals时遇到问题。问题是,我试图对单个键字段使用equals to only测试,并使用ArrayList。contains()测试是否存在具有正确字段的对象。这里有一个例子

public class TestClass  {
    private static class InnerClass{    
    private final String testKey;
    //data and such

    InnerClass(String testKey, int dataStuff) {
        this.testKey =testKey;
        //etc
    }
    @Override
    public boolean equals (Object in) {
        System.out.println("reached here");
        if(in == null) {
        return false;
        }else if( in instanceof String) {
        String inString = (String) in;
        return testKey == null ? false : testKey.equals(inString);
        }else {
        return false;
        }       
    }       
    }

    public static void main(String[] args) {    
    ArrayList<InnerClass> objectList = new ArrayList<InnerClass>();
    //add some entries
    objectList.add(new InnerClass("UNIQUE ID1", 42));
    System.out.println( objectList.contains("UNIQUE ID1")); 
    }    
}

让我担心的是,我不仅在输出上出错,而且没有得到“到达此处”的输出

有人知道为什么这个覆盖被完全忽略了吗?覆盖和内部类是否有一些我不知道的微妙之处

编辑: 有问题的网站,所以我似乎无法标记答案。 谢谢你的快速回复:是的,这是我的疏忽,那就是绳子。等于调用了thta,而不是我的自定义thta。我想现在是老式支票了


共 (6) 个答案

  1. # 1 楼答案

    通常,您还需要重写hashCode(),但这不是这里的主要问题。你有一个不对称的equals(..)方法。文件明确指出,它应该是对称的:

    It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.

    你观察到的是由于合同破裂而导致的意外行为

    创建一个实用程序方法,该方法迭代所有项,并在字符串上使用equals(..)进行验证:

    public static boolean containsString(List<InnerClass> items, String str) {
        for (InnerClass item : items) {
            if (item.getTestKey().equals(str)) {
               return true;
            }
        }
        return false;
    } 
    

    你可以用番石榴的Iterables.any(..)方法做类似的事情:

    final String str = "Foo";
    boolean contains = Iterables.any(items, new Predicate<InnerClass>() {
       @Override
       public boolean apply(InnerClass input){ 
           return input.getTestKey().equals(str);
       }
    }
    
  2. # 2 楼答案

    根据the JavaDoc of ^{},它被定义为返回true

    if and only if this list contains at least one element e such that (o==null ? e==null : o.equals(e)).

    请注意,此定义在o上调用equals,这是参数,而不是^{中的元素

    因此String.equals()将被调用,而不是InnerClass.equals()

    还请注意the contract for ^{}指出

    It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.

    但是您违反了这个约束,因为new TestClass("foo", 1).equals("foo")返回true,但是"foo".equals(new TestClass("foo", 1))将始终返回false

    不幸的是,这意味着您的用例(可以等同于另一个标准类的自定义类)不能以完全一致的方式实现

    如果您仍然想做这样的事情,您必须仔细阅读所有集合类的规范(有时是实现),并检查是否存在这样的缺陷

  3. # 3 楼答案

    您正在用一个String而不是InnerClass的参数调用contains

    System.out.println( objectList.contains("UNIQUE ID1"))
    

    在我的JDK中:

    public class ArrayList {
    
        public boolean contains(Object o) {
        return indexOf(o) >= 0;
        }
    
        public int indexOf(Object o) {
        if (o == null) {
            // omitted for brevity - aix
        } else {
            for (int i = 0; i < size; i++)
            if (o.equals(elementData[i])) // <<<<<<<<<<<<<<<<<<<<<<
                return i;
        }
        return -1;
        }
    }
    

    注意indexOf如何调用o.equals()。在你的例子中,o是一个String,所以你的objectList.contains将使用String.equals而不是InnerClass.equals

  4. # 4 楼答案

    虽然没有回答您的问题,但许多收藏使用hashcode()。你也应该覆盖它,以“同意”equals()

    实际上,您应该始终同时实现equalshashcode,并且它们应该始终保持一致。正如Object.equals()的javadoc所述:

    Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.

    具体地说,许多藏品都依赖于这一契约的维持——否则行为就没有定义

  5. # 5 楼答案

    如果您检查ArrayList的来源,您将看到它调用其他对象的equals。在您的例子中,它将调用equalsof String "UNIQUE ID1",这将检查其他对象是否为String类型,并仅返回false

    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
    
    public int indexOf(Object o) {
        ...     
        for (int i = 0; i < size; i++)
        if (o.equals(elementData[i]))
            return i;
        ...
        return -1;
    }
    

    对于您的案例,使用只包含idInnerClass调用contains

    objectList.contains(new InnerClass("UNIQUE ID1"))
    

    不要忘记为InnerClass实现equals,它只比较id

  6. # 6 楼答案

    你的实施是错误的。in参数不应该是String。它应该是一个InnerClass

    public boolean equals(Object o) {
      if (this == o) return true;
      if (!(o instanceof InnerClass) return false;
      InnerClass that = (InnerClass)o;
      // check for null keys if you need to
      return this.testKey.equals(that.testKey);
    }
    

    (注意instanceof null返回false,所以不需要先检查null)

    然后,您可以使用以下方法测试列表中是否存在等效的对象:

    objectList.contains(new InnerClass("UNIQUE ID1"));
    

    但是,如果您真的想通过字符串键检查InnerClass,为什么不使用Map<String,InnerClass>