Java中Map集合键(key)存放自定义数据类型出现的问题以及解决方案


Java中Map集合键(key)存放自定义数据类型出现的问题以及解决方案


问题阐述:

? 今日遇到一位Java学者,在学习 Map 集合通过键的值(key)取出相应的 值 时出现了 null 的问题,通常情况下使用 Map 集合时,我们将会使用 String 类型作为键的类型(为什么,我不多说,就是好用),但是这位学者把自己定义的引用数据类型(User)作为了 Map 的键的数据类型(key)进行了存放,在取出时遇到了问题。代码和运行结果如下:

自定义实体类

public class User implements Comparable {
 
    private String name;
    private int age;
    
    // 无参、有参构造、set()、get()、toString()、equals()、hashCode() 方法省略
    
    @Override
    public int compareTo(User o) {
        
        // compareTo 在比较关系下 存在 正大 负小 0相等 的逻辑应用 只是应用于 equals()比较时使用
        
        // 比较地址是否相等
        if (this == o) {
            return 0;
        }

        // 比较信息是否相等
        if (this.getName().equals(o.getName()) && this.getAge() == o.getAge()) {
            
            // 相等的情况下返回 0
            return 0;
        } else {
            
            // 信息不完全相等时 返回 非 0 的信息
            return 1;
        }

        
        // return 0;
    }
    
}

compareTo 在比较关系逻辑下 存在 正大 负小 0相等 的逻辑应用 只是应用于 equals()比较时使用

Map测试类

public static void main(String[] args) {
    
    // 准备 map 集合容器  键的类型为 User 自定义类型  值 为字符串类型
    Map map = new TreeMap<>();
    
    // 准备四个自定义对象
    User user = new User("孙悟空", 22);
    User user2 = new User("唐三藏", 33);
    User user3 = new User("猪八戒", 11);
    // 制造重复信息的对象
    User user4 = new User("孙悟空", 22);
    
    // 存放到 Map 集合中
    map.put(user, "孙悟空");
    map.put(user4, "孙悟空");
    map.put(user2, "唐三藏");
    map.put(user3, "猪八戒");
    
    // for循环打印信息
    for (User u : users) {
        
        // 首先打印 u 的信息 自动调用 User 的 toString() 方法
        // 然后打印 map.get(u) 通过键值来获取 值的信息
        System.out.println(u + "-----" + map.get(u));
    }
}

运行结果

User{name='孙悟空', age=22}-----null
User{name='唐三藏', age=33}-----唐三藏
User{name='猪八戒', age=11}-----猪八戒

分析一波

? 通常我们在使用 Map 集合时,存放数据时,键的数据类型我们一般定义为 String 类型,根据 Map 集合的特性:键的值是唯一性,存在键重复时,后者的数据覆盖前者的数据,实例代码和运行结果如下:

public static void main(String[] args) {
    
    Map map = new TreeMap<>();
    
    User user = new User("孙悟空", 22);
    User user2 = new User("唐三藏", 33);
    User user3 = new User("猪八戒", 11);
    User user4 = new User("孙悟空", 222222);
    
    map.put("孙悟空", user);
    map.put("孙悟空", user4);
    map.put("唐三藏", user2);
    map.put("猪八戒", user3);
    
    Set keySet = map.keySet();
    
    for (String s : keySet) System.out.println("键:" + s + "-----" + map.get(s));
}
键:唐三藏-----User{name='唐三藏', age=33}
键:孙悟空-----User{name='孙悟空', age=222222}
键:猪八戒-----User{name='猪八戒', age=11}

并不会取值的时候出现 null 值

阅读 String 的源码 String 中 compareTo() 重写的源码

? 源码中使用两者的字符串长度做减法获取正负数或者0(相等),Map在存储数据时,根据键的比较,当等于 0 时,判断为同一个键,则会后者覆盖前者,回忆学者的 compareTo() 的方法时 返回的结果只有 0 和 正数,进行代码修改。

@Override
public int compareTo(User o) {

        // return this.getName().compareTo(o.getName()); // 使用 String 类中已经重写好的 compareTo
	return this.getName().hashCode() - o.getName().hashCode();
}
User{name='唐三藏', age=33}-----唐三藏
User{name='孙悟空', age=22}-----孙悟空
User{name='猪八戒', age=11}-----猪八戒

问题解决

问题整体分析

  • 在使用 Map 的键(key)存放自定义数据类型时,必须实现 Comparable 接口,以及重写 compareTo() 方法。
  • Map 在 put 数据时,首先判断新存放的 键 是否存在,调用 键 所属的数据类型中的 compareTo() 方法比较,当我们自定义数据类型中 compareTo() 方法比较不清晰时,则会出现 Map 存放数据不正确的情况。

备注:初学者竟可能的规范开发,个性化开发遇到问题不要怕,前应后果找一找,想想规范开发下能跑通代码,自己的个性化开发为什么跑不通,阅读源码,查阅资料,锻炼自己,哈哈哈。