简体   繁体   English

使用Java ArrayList评估包含字符串的对象包含()

[英]Evaluating objects containing Strings with Java ArrayList contains()

I would like to do a deeper String check of Objects to be able to do the following: 我想对Objects进行更深入的String检查,以便能够执行以下操作:

List<MyObj> myList = new ArrayList<MyObj>() {{
    add(new MyObj("hello"));
    add(new MyObj("world"));
}};

System.out.println(myList.contains("hello")); // true
System.out.println(myList.contains("foo")); // false
System.out.println(myList.contains("world")); // true

But getting false on each one with the following full code 但是使用以下完整代码在每个上面都会出错

/* Name of the class has to be "Main" only if the class is public. */
class Ideone {
    public static class MyObj {
        private String str;
        private int hashCode;

        public MyObj(String str) {
            this.str = str;
        }

        public @Override boolean equals(Object obj) {
            System.out.println("MyObj.equals(Object)");
            if (obj == this) {
                return true;
            }

            if (obj instanceof String) {
                String strObj = (String) obj;
                return strObj.equals(str);
            }

            return false;
        }

        public @Override int hashCode() {
            if (hashCode == 0) {
                hashCode = 7;
                for (int i = 0; i < str.length(); ++i) {
                    hashCode = hashCode * 31 + str.charAt(i);
                }
            }

            return hashCode;
        }
    }

    public static final MyObj obj1 = new MyObj("hello");
    public static final MyObj obj2 = new MyObj("world");
    public static void main (String[] args) throws java.lang.Exception {
        List<MyObj> myList = new ArrayList<MyObj>() {{
            add(obj1);
            add(obj2);
        }};

        System.out.println(myList.contains("hello"));
        System.out.println(myList.contains("foo"));
        System.out.println(myList.contains("world"));
    }
}

If I'm right the List Object should use equals() and hashCode() to evaluate containing Objects. 如果我是对的,List Object应该使用equals()hashCode()来计算包含的对象。 So I @Override them and check their Strings additionally. 所以我@Override他们并另外检查他们的字符串。 But it never gets into equals() as there's no output MyObj.equals(Object) . 但它永远不会进入equals()因为没有输出MyObj.equals(Object)

java.util.ArrayList#indexOf is used internally in ArrayList for contains() . java.util.ArrayList#indexOf在ArrayList内部用于contains()

There is a check, 有一张支票,

o.equals(elementData[i])

So there is comparison of string with your object, so String.equals() is invoked for check of equality. 因此,字符串与您的对象进行比较,因此调用String.equals()以检查相等性。

You are not fulfilling the equals contract at all: 你根本没有履行equals合同:

The equals method implements an equivalence relation on non-null object references: equals方法在非null对象引用上实现等价关系:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true. 它是自反的:对于任何非空引用值x,x.equals(x)应该返回true。 Yours is not reflexive . 你的不是反身
  • 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. 它是对称的:对于任何非空引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)才应返回true。 Yours is not symmetric. 你的不对称。
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true. 它是传递性的:对于任何非空引用值x,y和z,如果x.equals(y)返回true而y.equals(z)返回true,则x.equals(z)应返回true。 Yours is not transitive 你的不是传递性的
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified. 它是一致的:对于任何非空引用值x和y,x.equals(y)的多次调用始终返回true或始终返回false,前提是不修改在对象的equals比较中使用的信息。
  • For any non-null reference value x, x.equals(null) should return false. 对于任何非空引用值x,x.equals(null)应返回false。

So without respecting the contract for the method you can't expect intended behavior. 因此,如果不尊重方法的合同,您就不能指望预期的行为。

Just for example, what guarantees you that equals is going to be called on the object contained in the ArrayList and not in the other way, eg "hello".equals(new MyObj("hello")) . 例如,什么保证你将在ArrayList中包含的对象上调用equals,而不是以其他方式"hello".equals(new MyObj("hello")) ,例如"hello".equals(new MyObj("hello")) You have no guarantees about it but since equals is normally supposed to be symmetric than you shouldn't mind. 你不能保证它,但是因为equals通常被认为是对称的而不是你不应该介意的。

As pointed out by others, the problem is that your equals method is never called. 正如其他人所指出的那样,问题是你的 equals方法永远不会被调用。 When you invoke myList.contains("hello") , ArrayList checks whether "hello".equals(<MyObj>) , not the other way around. 当你调用myList.contains("hello")ArrayList检查"hello".equals(<MyObj>) ,而不是相反。

Instead, I recommend implementing your equals method properly , so that two MyObject instances with equal value are considered equal, and then create a helper method like this: 相反,我建议正确实现你的equals方法,以便将两个具有相等value MyObject实例视为相等,然后创建一个这样的辅助方法:

public boolean myContains(List<MyObj> list, String value) {
    return list.contains(new MyObj(value));
}
List<MyObj> myList = new ArrayList<MyObj>()

is a list of MyObj , so you need to use MyObj while checking myList.contains : 是的清单MyObj ,所以你需要使用MyObj同时检查myList.contains

System.out.println(myList.contains(new MyObj("hello")));
System.out.println(myList.contains(new MyObj("foo")));
System.out.println(myList.contains(new MyObj("world")));

You're asking the List to compare a String to a MyObj ... They are never going to be "equal". 你要求List将StringMyObj进行比较......它们永远不会“相等”。 Try a map instead: 请尝试使用地图:

Map<String, MyObj> map = new HashMap<String, MyObj>() {{
    put("hello", new MyObj("hello"));
    put("world", new MyObj("world"));
}};

Then you can use: 然后你可以使用:

if (map.containsKey("hello"))

and to get the corresponding object: 并获得相应的对象:

MyObj o = map.get("hello"); // get() returns null if key not found

It is not equals of your object called when contains executes but the one from String class. 它不是在包含执行时调用的对象的等同,而是来自String类的对象。 And String implementation checks with instanceof whether the class is a String to perform String-like operations to determine the answer. 和String实现检查instanceof是否该类是一个String来执行类似String的操作来确定答案。 If object is not a String it will return false; 如果object不是String,则返回false;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM