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

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

一、前言

先说结论:

  • 数组不能直接当做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

网络异常,图片无法展示
|

测试修改后的代码

网络异常,图片无法展示
|

网络异常,图片无法展示
|



目录
相关文章
|
5月前
|
索引
ES5常见的数组方法:forEach ,map ,filter ,some ,every ,reduce (除了forEach,其他都有回调,都有return)
ES5常见的数组方法:forEach ,map ,filter ,some ,every ,reduce (除了forEach,其他都有回调,都有return)
|
3月前
|
存储 Java API
Java交换map的key和value值
通过本文介绍的几种方法,可以在Java中实现Map键值对的交换。每种方法都有其优缺点,具体选择哪种方法应根据实际需求和场景决定。对于简单的键值对交换,可以使用简单遍历法或Java 8的Stream API;对于需要处理值不唯一的情况,可以使用集合存储或Guava的Multimap。希望本文对您理解和实现Java中的Map键值对交换有所帮助。
68 1
|
4月前
|
前端开发 JavaScript 索引
JavaScript 数组常用高阶函数总结,包括插入,删除,更新,反转,排序等,如map、splice等
JavaScript数组的常用高阶函数,包括遍历、插入、删除、更新、反转和排序等操作,如map、splice、push、pop、reverse等。
33 0
|
5月前
数组方法中的`forEach()`方法和`map()`方法有什么区别?
数组方法中的`forEach()`方法和`map()`方法有什么区别?
|
5月前
|
JavaScript 前端开发
JavaScript 中 五种迭代数组的方法 every some map filter forEach
本文介绍了JavaScript中五种常用数组迭代方法:every、some、filter、map和forEach,并通过示例代码展示了它们的基本用法和区别。
|
6月前
|
JavaScript 前端开发 索引
JS中常用的数组迭代方法(filter,forEach,map,every,some,find,findIndex)
这段代码和说明介绍了JavaScript中数组的一些常用方法。函数接收三个参数:`item`(数组项的值)、`index`(项的位置,可选)和`array`(数组本身,可选)。示例展示了如何使用`filter()`过滤非空项、`forEach()`遍历数组、`map()`处理并返回新数组、`every()`检查所有元素是否满足条件、`some()`检查是否存在满足条件的元素、`find()`获取首个符合条件的元素值以及`findIndex()`获取其索引位置。这些方法都不会修改原数组。
JS中常用的数组迭代方法(filter,forEach,map,every,some,find,findIndex)
|
7月前
|
JavaScript API
js【最佳实践】遍历数组的八种方法(含数组遍历 API 的对比)for,forEach,for of,map,filter,reduce,every,some
js【最佳实践】遍历数组的八种方法(含数组遍历 API 的对比)for,forEach,for of,map,filter,reduce,every,some
137 1
|
8月前
|
存储 Java API
探讨Java中交换Map的Key和Value值的技术
探讨Java中交换Map的Key和Value值的技术
77 2
|
8月前
|
JavaScript 前端开发
JavaScript 数组的函数 map/forEach/reduce/filter
JavaScript 数组的函数 map/forEach/reduce/filter
|
8月前
|
JSON Go 数据格式
Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】(4)
Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】