Java中Set的contains()方法

Java中Set的contains()方法 —— hashCode与equals方法的约定及重写原则
最近写项目的时候遇到了这个问题,就是我在一个ArrayList里放了一个很多个vo,每当我要在里面添加vo的时候,我都要判断一下,这个list里是否已经存在,若是存在,则不添加。虽然知道是用contains()方法,但用了之后发现一直都是false,就是无论怎么判断都是会插进去。

为了解决这个问题,看了很多相关的博文,感觉这篇翻译的文章真的解决了问题,文章后面附着我最后是如何解决的。

因为解决contains()的问题要牵扯到hashCode()和equals()方法,这有篇文章,可以帮助了解一下:
equals()与hashCode()方法协作约定

本文主要讨论 集合Set 中存储对象的 hashCode 与 equals 方法应遵循的约束关系.

新手对Set中contains()方法的疑惑

import java.util.HashSet;

class Dog{
    String color;

    public Dog(String s){
        color = s;
    }   
}

public class SetAndHashCode {
    public static void main(String[] args) {
        HashSet<Dog> dogSet = new HashSet<Dog>();
        dogSet.add(new Dog("white"));
        dogSet.add(new Dog("white"));

        System.out.println("We have " + dogSet.size() + " white dogs!");

        if(dogSet.contains(new Dog("white"))){
            System.out.println("We have a white dog!");
        }else{
            System.out.println("No white dog!");
        }   
    }
}

上述代码的输出为:

We have 2 white dogs!
No white dog!

程序中添加了两只白色的小狗到集合dogSet中. 且 size()方法显示有2只白色的小狗.但为什么用 contains()方法来判断时却提示没有白色的小狗呢?

Set的contains(Object o) 方法详解
Java的API文档指出: 当且仅当 本set包含一个元素 e,并且满足(onull ? enull : o.equals(e))条件时,contains()方法才返回true. 因此 contains()方法 必定使用equals方法来检查是否相等.

需要注意的是: set 中是可以包含 null值的(常见的集合类都可以包含null值). 所以如果添加了null,然后判断是否包含null,将会返回true,代码如下所示:

HashSet<Dog> a = new HashSet<Dog>();
a.add(null);
if(a.contains(null)){
    System.out.println("true");
}

Java的根类Object定义了 public boolean equals(Object obj) 方法.因此所有的对象,包括数组(array,[]),都实现了此方法。

在自定义类里,如果没有明确地重写(override)此方法,那么就会使用Object类的默认实现.即只有两个对象(引用)指向同一块内存地址(即同一个实际对象, x==y为true)时,才会返回true。

如果把Dog类修改为如下代码,能实现我们的目标吗?

class Dog{
    String color;

    public Dog(String s){
        color = s;
    }

    //重写equals方法, 最佳实践就是如下这种判断顺序:
    public boolean equals(Object obj) {
        if (!(obj instanceof Dog))
            return false;   
        if (obj == this)
            return true;
        return this.color == ((Dog) obj).color;
    }

}

英文答案是: no.

问题的关键在于 Java中hashCode与equals方法的紧密联系. hashCode() 是Object类定义的另一个基础方法.

equals()与hashCode()方法之间的设计实现原则为:
如果两个对象相等(使用equals()方法),那么必须拥有相同的哈希码(使用hashCode()方法).

即使两个对象有相同的哈希值(hash code),他们不一定相等.意思就是: 多个不同的对象,可以返回同一个hash值.

hashCode()的默认实现是为不同的对象返回不同的整数.有一个设计原则是,hashCode对于同一个对象,不管内部怎么改变,应该都返回相同的整数值.

在上面的例子中,因为未定义自己的hashCode()实现,因此默认实现对两个对象返回两个不同的整数,这种情况破坏了约定原则。

解决办法

class Dog{
    String color;

    public Dog(String s){
        color = s;
    }

    //重写equals方法, 最佳实践就是如下这种判断顺序:
    public boolean equals(Object obj) {
        if (!(obj instanceof Dog))
            return false;   
        if (obj == this)
            return true;
        return this.color == ((Dog) obj).color;
    }

    public int hashCode(){
        return color.length();//简单原则
    }
}

但是上面的hashCode实现,要求Dog的color是不变的.否则会出现如下的这种困惑:

import java.util.HashSet;
import java.util.Set;
public class TestContains {


    public static final class Person{
        private String name = "";
        public Person(String n) {
            setName(n);
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = (name==null)? "" : name;
        }
        @Override
        public int hashCode() {
            // 请考虑是否值得这么做,因为此时name是会变的.
            return name.length();
            // 推荐让name不可改变
        }
        @Override
        public boolean equals(Object obj) {
            if(!(obj instanceof Person)){
                return false;
            }
            if(obj == this){
                return true;
            }
            return this.name.equals(((Person)obj).name);
        }
    };

    public static void main(String[] args) {
        Set<Person> persons = new HashSet<Person>();
        //
        Person person = new Person("tiemao");
        persons.add(person);
        // 修改name, 则依赖hash的集合可能失去作用
        person.setName("ren");
        // 同一个对象,居然是false,原因是我们重写了hashCode,打破了hashCode不变的基本约定
        boolean has = persons.contains(person);
        int size = persons.size();
        System.out.println("has="+has); // has=false.
        System.out.println("size="+size);// size=1
    }
    }

以下是我遇到的问题:

List userlis = new ArrayList<>();
1
这里写图片描述

UserVo是一个类,userlist是一个ArrayList,我在每次往userlis中add数据的时候,我都要先判断一下,userlis中是不是已经存在同样的数据,如果存在,则不插入,如果不存在,我就add就去。

起初我没有重写equals()和hashCode()方法,每次的判断都是false,就是都能插进去。以下是在UserVo中重写了这两个方法,问题完美解决。

这里写图片描述

其中ID是UserVo是唯一标识符,比较两个Vo是否相等比较id就好了,当然,每个人写的都不相同,仅供参考。

翻译人员:铁锚
翻译时间: 2013年11月5日
翻译连接:Java中Set的contains()方法
原文链接: Java hashCode() and equals() Contract for the contains(Object o) Method of Set

参考文章:
http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html

相关阅读
Java equals() and hashCode() Contract

HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap

Java: Find all callers of a method – get all methods that call a particular method

转:
https://blog.csdn.net/violet_echo_0908/article/details/50152915

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值