zoukankan      html  css  js  c++  java
  • Java中的Set对象去重

    前言部分

    Set<T> 去重相信大家一定不陌生,尤其是在 Set<String>Set<Integer> 等等,但是在使用 Set<实体> ,在不重写 equals()、hashCode() 方法情况下,直接使用貌似并不能生效。

    所以想要 Set<实体> 实现去重,核心部分在实体中重写 equals()、hashCode() 方法。

    如下以 User 实体为例,进行测试。

    代码部分

    测试代码:

    public static void main(String[] args) {
       Set<User> userSet = new HashSet<User>(){{
           add(new User("张三",10));
           add(new User("张三",20));
           add(new User("张三",10));
       }};
        userSet.forEach(user -> {
            System.out.println(String.format("name: %s, age:%s",user.getName(),user.getAge()));
        });
    }

    打印结果:

    name: 张三, age:20
    name: 张三, age:10

    实体对象(User.java): 重写了 equals()、hashCodd() 方法。

    public class User {

        public User(String name, Integer age){
            this.name = name;
            this.age = age;
        }

        /** 姓名 **/
        private String name;

        /** 年龄 **/
        private Integer age;

        省略get、set方法...

        /**
         * 重写equals方法,如果对象类型是User,先比较hashcode,一致的场合再比较每个属性的值
         */
        @Override
        public boolean equals(Object obj) {
            System.out.println("调用equals方法,当前的hashCode为:"+hashCode());
            /** 对象是 null 直接返回 false **/
            if (obj == null) {
                return false;
            }
            /** 对象是当前对象,直接返回 true **/
            if (this == obj) {
                return true;
            }
            /** 判断对象类型是否是User **/
            if (obj instanceof User) {
                User vo = (User) obj;
                /** 比较每个属性的值一致时才返回true **/
                /** 有几个对象就要比较几个属性 **/
                if (vo.name.equals(this.name) && vo.age.equals(this.age)) {
                    return true;
                }
            }
            return false;
        }

        /**
         * 重写hashcode方法,返回的hashCode一样才再去比较每个属性的值
         */
        @Override
        public int hashCode() {
            return this.getName().hashCode() * this.getAge().hashCode();
        }

    }

    解释部分

    为什么 Set<String>Set<Integer> 就可以直接实现去重,而 Set<实体> 就不可以,反而要重写 equals()、hashCode() 方法才能实现,更甚者是,只重写 equals() 方法,而不重写 hashCode() 方法都没法完成去重~

    大家对这个问题有过疑惑吗?

    1、HashSet 添加数据过程

    HashSet 的底层实现,相信大家都清楚是 HashMap 吧?我们在 add() 数据时,其实一层层找,最终是调的 HashMap 的 put() 方法,如下是 HashSet 的 add() 方法,其中 map 为 HashMap。

    我们再点一层找到 HashMap 的 put() 方法:

    如上图所示,通过 putVal() 方法我们大致有了个概念了,判断是否为旧值就是对 hash 值、key 值进行比较。

    hash 值比较自然调用的事 hashCode() 方法,而 key 值的比较实用的是 equals() 方法。

    了解到这基本就可以看出 hashCode() 、equals() 方法对于去重的重要性了。

    2、Set<单属性> 可以直接使用去重

    那么接下来我们就可以来看看 Set<单属性>(单属性:String、Integer等),为什么直接使用就可以去重了。

    我们以 String 为例,假设有两个字符串 a、b,如下:

    String a = "123";
    String b = "123";
    System.out.println("a.hashCode:"+a.hashCode());
    System.out.println("b.hashCode:"+b.hashCode());
    System.out.println(a.equals(b));

    打印结果如下:

    a.hashCode:48690
    b.hashCode:48690
    true

    很显然,在没有重写 hashCode() 、equals() 方法时,字符串 a、b 的 hashCode,equalse() 是一致的,那么这两个就可以视为一个对象,所以用在 Set 里面就可以直接去重。

    但是为什么会一致呢?

    任何对象在不重写 equals()、hashcode() 的情况下,使用的是 Object 对象的 equals() 方法和 hashcode() 方法,而重点就是,默认的 equals() 方法判断的是两个对象的引用指向的是不是同一个对象;而 hashcode 也是根据对象地址生成一个整数数值;

    显然字符串 a、b 这两个条件都满足,所以对于 Set 来说就是一个对象的概念。

    3、Set<实体> 去重

    但是换到对于实体对象就行不通了,我们再来套 Object 的 equals()、hashCode() 方法。

    当我们 new User() 对象时,两个对象的地址引用肯定是不同的;其次 hashcode 是根据对象地址生成的,这样显然也不同,所以对于 Set 来说,那么去重就行不通。

    因此,想要让 Set<实体> 实现去重效果,那么就需要重写 equals() 、hashCode() 方法。

    只有两个对象的 hashCode() 方法的值一致,且 equalse() 方法返回 true,那么这对于 Set<实体> 来说就可以看做一个对象, 如果两者只满足一个是不可以的(只重写一个),举个例子:

    equales()重写,hashCode()不重写

    @Override
    public boolean equals(Object obj) {
        return true;
    }

    //@Override
    //public int hashCode() {
    //    return this.getName().hashCode() * this.getAge().hashCode();
    //}

    执行代码:

    Set<User> userSet = new HashSet<User>(){{
       add(new User("张三",10));
       add(new User("张三",20));
       add(new User("张三",10));
    }};

    userSet.forEach(user -> {
        System.out.println(String.format("name: %s, age:%s",user.getName(),user.getAge()));
    });

    打印内容:

    name: 张三, age:10
    name: 张三, age:10

    equales()不重写,hashCode()重写

    //@Override
    //public boolean equals(Object obj) {
    //    return true;
    //}

    @Override
    public int hashCode() {
      return this.getName().hashCode() * this.getAge().hashCode();
    }

    执行代码+打印内容如上:

    name: 张三, age:10
    name: 张三, age:10

    总结

    总之,要想保证 Set<实体> 实现去重,就需要两个实体 “一致”,这里的一致是只需要满足如下两个条件:

    • 重写 hashCode() 方法,确保两者 hashcode 一致,比如使用属性相乘或者相加。
    • 重写 equals() 方法,相同对象、属性值相同对象皆为相等。

    通过上面这些例子也能看出重写 equals 方法,就必须重写 hashCode 的重要性,因为只重写 equals() 不一定能满足预期相等的效果。

    如下是阿里巴巴开发手册,关于 hashCode 和 equals 的处理规则:

    希望这篇文章对你有所帮助。博客园持续更新,欢迎关注。

    博客园:https://www.cnblogs.com/niceyoo

  • 相关阅读:
    springcloud随便写点
    简单的数组排序
    简单wait(),notify()方法
    热词
    mysql日期函数
    springboot和jsp,以及引入jquery
    用户,权限,角色的关系
    通过字节码获取到的方法
    数据库并发事物
    在spring的业务层获取request,response
  • 原文地址:https://www.cnblogs.com/niceyoo/p/13962100.html
Copyright © 2011-2022 走看看