前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AI 帮我避免了 Map 计算哈希值的一个大坑

AI 帮我避免了 Map 计算哈希值的一个大坑

作者头像
明明如月学长
发布2023-05-18 09:34:39
2890
发布2023-05-18 09:34:39
举报

一、背景

一个业务场景需要对 Map 计算哈希值作为缓存 key 的构成部分。思路是将 Map 转为字符串,然后对字符串取 Hash 值。 不过这里有个很大的坑,即 Map 中 Entry 的顺序问题,即仅仅 Key 的顺序不同而值相同时哈希值应该相同。 如果使用 AI 大概率会注意到这个问题,如果直自己写很容易忽略这个问题。

二、问题复现

构造两个“键值”等价的 Map,发现其 hash 值不同。

代码语言:javascript
复制
import com.google.common.base.Charsets;
import com.google.common.hash.Hashing;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        Map<String, String> map1 = new LinkedHashMap<>();
        map1.put("key1", "value1");
        map1.put("key2", "value2");

        Map<String, String> map2 = new LinkedHashMap<>();
        map2.put("key2", "value2");
        map2.put("key1", "value1");

        String mapString1 = map1.entrySet()
                .stream()
                .map(e -> e.getKey() + "=" + e.getValue())
                .collect(Collectors.joining(", "));

        String mapString2 = map2.entrySet()
                .stream()
                .map(e -> e.getKey() + "=" + e.getValue())
                .collect(Collectors.joining(", "));

        String hash1 = Hashing.sha256()
                .hashString(mapString1, Charsets.UTF_8)
                .toString();

        String hash2 = Hashing.sha256()
                .hashString(mapString2, Charsets.UTF_8)
                .toString();

        System.out.println("The hash of the first map is: " + hash1);
        System.out.println("The hash of the second map is: " + hash2);
        System.out.println(hash1.equals(hash2));
    }
}

输出的结果:

The hash of the first map is: 500ccf8d28b6857c91a01a1e6d751e89d3872af97a8079cecd1dce0497ab084d The hash of the second map is: d7d67e36f33ebf3f51e1d0c6e016a17a2d839e897951bb98fa5cd190a135b331 false

有些同学可能会说,你这里用到的是 LinkedHashMap 当然会有问题。 但问题在于,通常我们定义接口时,参数为 Map 类型,两个“键值”等价,其 Entry 的顺序未必相等。

三、解决办法

对 Entry 或者 map 后的字符串排序,保证“键值”等价的两个 Map 在 hash 前拼接出的字符串相同。

代码语言:javascript
复制
import com.google.common.base.Charsets;
import com.google.common.hash.Hashing;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

public class MapHashDemo {
    public static void main(String[] args) {
        Map<String, String> map1 = new LinkedHashMap<>();
        map1.put("key1", "value1");
        map1.put("key2", "value2");

        Map<String, String> map2 = new LinkedHashMap<>();
        map2.put("key2", "value2");
        map2.put("key1", "value1");


        String hash1 = getSha256(map1);

        String hash2 = getSha256(map2);

        System.out.println("The hash of the first map is: " + hash1);
        System.out.println("The hash of the second map is: " + hash2);
        System.out.println(hash1.equals(hash2));
    }


    private static String getSha256(Map<String, String> map) {
        String mapString1 = map.entrySet()
                .stream()
                .map(e -> e.getKey() + "=" + e.getValue())
                .sorted()
                .collect(Collectors.joining(", "));

        return Hashing.sha256()
                .hashString(mapString1, Charsets.UTF_8)
                .toString();
    }
}

The hash of the first map is: 500ccf8d28b6857c91a01a1e6d751e89d3872af97a8079cecd1dce0497ab084d The hash of the second map is: 500ccf8d28b6857c91a01a1e6d751e89d3872af97a8079cecd1dce0497ab084d true

这样哪怕 Map 中的 Entry 顺序不一致,计算出来的 sha256 的哈希值也相同。

三、AI 助力

另外,再次倡议大家日常工作和学习中可以尝试使用 ChatGPT 、 New Bing 这类 AI 工具,这类简单的代码可以让它们给出 Demo,它们给出的 Demo 时有很大概率可以给出正确的代码。

如何使用 com.google.common.hash.Hashing 计算一个 Map 的 hash 值?

ChatGPT 给出的答案:


com.google.common.hash.Hashing是 Google Guava库中的一个类,它提供了一系列的哈希函数。 计算一个Map的哈希值,我们可以先将Map的key和value转化为字符串,然后使用Hashing的哈希函数进行计算。 以下是一个简单的例子,假设我们有一个 Map :

代码语言:javascript
复制
import com.google.common.base.Charsets;
import com.google.common.hash.Hashing;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("key1", "value1");
        map.put("key2", "value2");

        String mapString = map.entrySet()
                              .stream()
                              .map(e -> e.getKey() + "=" + e.getValue())
                              .sorted()
                              .collect(Collectors.joining(", "));

        String hash = Hashing.sha256()
                             .hashString(mapString, Charsets.UTF_8)
                             .toString();

        System.out.println("The hash of the map is: " + hash);
    }
}

这个例子中,我们首先将Map的每一项(key和value)转化为一个字符串,格式为"key=value",然后将这些字符串排序并用逗号连接起来,生成一个总的字符串。最后,我们用Guava的Hashing.sha256()函数计算这个字符串的哈希值。

**需要注意的是,由于 Map 的元素没有固定的顺序,所以在生成字符串的时候我们需要对其进行排序,以保证相同的 Ma p能够得到相同的哈希值。**同时,使用哈希函数时,需要选择适当的字符集,这里我们选择了UTF-8。

此外,还要注意哈希冲突的问题,尽管对于大多数情况来说,哈希冲突的概率非常小,但理论上还是存在可能的。所以在使用哈希值时,需要根据具体情况判断是否可以接受这种潜在的冲突风险。

四、总结

大家在对 Map 计算其哈希值作为缓存 key 时,需要特别注意这个问题。 建议大家在日常开发中一些简单的代码示例,一些验证性的代码,推荐大家优先让 AI 帮我们编写,有时候会有意外收获。


创作不易,如果本文对你有帮助,欢迎点赞、收藏加关注,你的支持和鼓励,是我创作的最大动力。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-05-16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 二、问题复现
  • 三、解决办法
  • 三、AI 助力
  • 四、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档