龙空技术网

从数组当做map的key引发的思考

陈三岁98 221

前言:

当前朋友们对“javahashmapkey”大致比较关注,兄弟们都需要了解一些“javahashmapkey”的相关文章。那么小编在网上网罗了一些对于“javahashmapkey””的相关文章,希望兄弟们能喜欢,姐妹们快快来了解一下吧!

一、前言

先说结论:

数组不能直接当做map的key去使用也不能使用 String.valueOf(int[] ints)的方法将数组转为String去当做map的key使用

在今天刷力扣题的过程中发现了以下的问题,算是很基础的知识了,但是在日常的代码开发过程中并没有实际的碰到过这种情况, 大概的的代码和运行结果如下所示:

public static void main(String[] args) {    // 设置两个相同的数组    int[] ints1 = {1};    int[] ints2 = {1};    // 使用 String.valueOf方法获取两个 String对象    String s = String.valueOf(ints1);    String t = String.valueOf(ints2);    // 为 map1赋值    Map<int[], Integer> map1 = new HashMap<>();    map1.put(ints1, 1);    // 为 map2赋值    Map<String, Integer> map2 = new HashMap<>();    map2.put(s, 1);    System.out.println(map1.containsKey(ints2));    System.out.println("=====分割线=====");    System.out.println(map2.containsKey(t));}复制代码
二、LeetCode 49. 字母异位词分组题目信息如下所示解题思路定义返回的 list集合 result新建一个 Map<String, List<String>>,key为判断单词为字母异位词的标志点,value为字母异位词相同的集合遍历 List集合获取字符串 str设置一个 char数组,该数组的下标表示字母ASCII表 - ‘a’之后的数值将当前字符串转为 char数组并遍历,使字母对应的 array数组下标++由上面的过程可知,如果这个单词的字母组成相同,那么他的 array数组肯定也相同所以我们可将数组当做 map的 key然后判断当前 key在map中有没有没有就新增,value为当前字符串有则取出 value,把当前字符串加入 value最后遍历 map,将 value加入到最初创建的返回集合 result中返回

大家可以看到,在上面的解题思路中,有一步我进行了加粗,没错,就是那里出现了问题,代码实现及测试结果如下图所示:

问题分析

由上面的测试结果可以看出,出参肯定是错误的, eat和 tea肯定是字母异位词, 测试案例也是 LeetCode题中的示例1, 大家也可以对照一下

那么问题来了,为什么我们的 array数据结构都一致,但是在 map.put时计算出来的hash不一致呢,于是我打了个断点到 HashMap的源码里面看到了以下的情况

我们注意看这个 key,他是一个内存地址指向啊,下图是当 map的 key为 String的情况下,key就是我们设置的值

这个时候我猜你也想到原因了,数组的 toString方法返回的就是当前对象的内存地址,那么每一次我们遍历当前字符串的时候都会对 array数组进行一次 new对象的操作,所以,我们每一次操作的都不是同一个对象,这也导致了 key的不一致

为了验证我们的想法,我对齐进行了验证,在不改变原有对象的情况下去改变数组的值,发现他们的哈希值是一样的,这也验证了我们的 Map.put方法中, key哈希值的计算是依赖于对象的 toString方法的

迈入另一个坑

问题找到了,我就想既然数组的 toString方法 java没有为我们重写,惹不起还能躲不起?

我直接把当前数组转换为 String不就好了,使用我们真实的数值当做 key去计算 hash,而不是地址,于是我买入了第二个坑, 我使用了 String.value()方法

代码如下图所示, 代码在红框位置进行了相应的更改

可是我运行代码之后发现,问题并没有解决

有了之前的分析,这次我们可以确定还是 内存地址的问题,但是我点了以下 String.value()方法之后,发现不是所有的数组的 toString方法都没有进行重写

于是我对其进行了验证, 可以看到,我们将字符串转化为 char数组之后,它打印出来的就是当前字符串,说明了 java内部是对 char数组的 toString方法进行了重写的

解决该问题

那么我又想除了 String.value()方法之外还有什么方法能让我当前的数组转换为 String对象,java.util包下的Arrays这个工具类映入我的脑海

在这个工具类下有一个方法 Arrays.toString()方法,是专门用来对数组进行 toString格式转换的,我们修改我们的代码,发现问题解决了

代码展示

    public static List<List<String>> groupAnagrams(String[] strs) {        // 定义返回值        List<List<String>> result = new ArrayList<>();        // 存储相同字母的单词        Map<String, List<String>> map = new HashMap<>();        // 遍历 strs        for (String str : strs) {            int[] array = new int[26];            for (char c : str.toCharArray()) {                array[c - 'a']++;            }            List<String> strings;            String s = Arrays.toString(array);            if (map.containsKey(s)) {                strings = map.get(s);            }else{                strings = new ArrayList<>();            }            strings.add(str);            map.put(s, strings);        }        for (Map.Entry<String, List<String>> listEntry : map.entrySet()) {            result.add(listEntry.getValue());        }        return result;    }复制代码
LeetCode提交记录

讲实话,不推荐这种方式,因为我们可以在 str.toCharArray()的时候对 数组进行排序, 然后再转回字符串,当做 key存储到 map中,但是我第一想法就是使用文章介绍的方式做,大佬们勿喷

三、总结测试了几种数组,发现只有char数组是重写了 toString方法的, 测试结果可见下图String.valueOf()方法除了传递 char[] 之外, 其他的数组返回值都是内存地址Arrays工具类一定要常用,尽量保持熟悉,里面有很多使用的方法, 例如: toString()方法、 equals()方法和 sort方法LeetCode 49题建议使用 Arrays.sort()方法对字符串转成的 char[]进行重新排序,测试代码如下图

测试toString

测试修改后的代码

来源:

标签: #javahashmapkey