快来,我悄悄的给你说几个HashCode的破事。 (2)

简介: 快来,我悄悄的给你说几个HashCode的破事。 (2)

31x+y=31a+b 可以变形为:


31x-31a=b-y。


即,31(x-a)=b-y。


这个时候就清晰很多了,很明显,上面的等式有一个特殊解:


x-a=1,b-y=31。


因为,由上可得:对于任意两个字符串 xy 和 ab,如果它们满足 x-a=1,即第一个字符的 ASCII 码值相差为 1,同时满足 b-y=31,即第二个字符的 ASCII 码值相差为 -31。那么这两个字符的 hashCode 一定相等。


都已经说的这么清楚了,这样的组合对照着 ASCII 码表来找,不是一抓一大把吗?


Aa 和 BB,对不对?


Ab 和 BC,是不是?


Ac 和 BD,有没有?



image.png


好的。现在,我们可以生成两个 HashCode 一样的字符串了。


我们在稍微加深一点点难度。假设我要构建 2 个以上 HashCode 一样的字符串该怎么办?


我们先分析一下。


Aa 和 BB 的 HashCode 是一样的。我们把它两一排列组合,那不还是一样的吗?


比如这样的:AaBB,BBAa。


image.png


再比如我之前《震惊!ConcurrentHashMap里面也有死循环?》这篇文章中出现过的例子,AaAa,BBBB:


你看,神奇的事情就出现了。


我们有了 4 个 hashCode 一样的字符串了。


image.png


有了这 4 个字符串,我们再去和 Aa,BB 进行组合,比如 AaBBAa,BBAaBB......


4*2=8 种组合方式,我们又能得到 8 个 hashCode 一样的字符串了。


image.png


等等,我好像发现了什么规律似的。


如果我们以 Aa,BB 为种子数据,经过多次排列组合,可以得到任意个数的 hashCode 一样的字符串。字符串的长度随着个数增加而增加。


文字我还说不太清楚,直接 show you code 吧,如下:


public class CreateHashCodeSomeUtil {
    /**
     * 种子数据:两个长度为 2 的 hashCode 一样的字符串
     */
    private static String[] SEED = new String[]{"Aa", "BB"};
    /**
     * 生成 2 的 n 次方个 HashCode 一样的字符串的集合
     */
    public static List<String> hashCodeSomeList(int n) {
        List<String> initList = new ArrayList<String>(Arrays.asList(SEED));
        for (int i = 1; i < n; i++) {
            initList = createByList(initList);
        }
        return initList;
    }
    public static List<String> createByList(List<String> list) {
        List<String> result = new ArrayList<String>();
        for (int i = 0; i < SEED.length; ++i) {
            for (String str : list) {
                result.add(SEED[i] + str);
            }
        }
        return result;
    }
}



通过上面的代码,我们就可以生成任意多个 hashCode 一样的字符串了。

就像这样:


image.png


所以,别再问出这样的问题了:


image.png


image.png


有了这些 hashCode 一样的字符串,我们把这些字符串都放到HashMap 中,代码如下:



public class HashMapTest {
    public static void main(String[] args) {
        Map<String, String> hashMap = new HashMap<String, String>();
        hashMap.put("Aa", "Aa");
        hashMap.put("BB", "BB");
        hashMap.put("AaAa", "AaAa");
        hashMap.put("AaBB", "AaBB");
        hashMap.put("BBAa", "BBAa");
        hashMap.put("BBBB", "BBBB");
        hashMap.put("AaAaAa", "AaAaAa");
        hashMap.put("AaAaBB", "AaAaBB");
        hashMap.put("AaBBAa", "AaBBAa");
        hashMap.put("AaBBBB", "AaBBBB");
        hashMap.put("BBAaAa", "BBAaAa");
        hashMap.put("BBAaBB", "BBAaBB");
        hashMap.put("BBBBAa", "BBBBAa");
        hashMap.put("BBBBBB", "BBBBBB");
    }
}



最后这个 HashMap 的长度会经过两次扩容。扩容之后数组长度为 64:



image.png


但是里面只被占用了三个位置,分别是下标为 0,31,32 的地方


image.png


画图如下:


image.png


看到了吧,刺不刺激,长度为 64 的数组,存 14 个数据,只占用了 3 个位置。


这空间利用率,也太低了吧。


所以,这样就算是 hack 了 HashMap。恭喜你,掌握了一项黑客攻击技术:hash 冲突 Dos 。


如果你想了解的更多。可以看看石头哥的这篇文章:《没想到 Hash 冲突还能这么玩,你的服务中招了吗?》


看到上面的图,不知道大家有没有觉得有什么不对劲的地方?


如果没有,那么我再给你提示一下:数组下标为 32 的位置下,挂了一个长度为 8 的链表。


是不是,恍然大悟了。在 JDK 8 中,链表转树的阈值是多少?

目录
相关文章
|
11月前
|
缓存 安全 Java
【C++从0到王者】第三十二站:异常
【C++从0到王者】第三十二站:异常
45 0
|
5月前
|
NoSQL Java 中间件
百度面试,跪了!凉经分享
百度面试,跪了!凉经分享
81 0
|
数据安全/隐私保护 图形学 Windows
推荐五款宝藏软件,身为宝藏男孩和宝藏女孩的你,不试一下吗?
今天带来五款宝藏软件,身为宝藏男孩和宝藏女孩的你们,不试一下吗?
189 0
推荐五款宝藏软件,身为宝藏男孩和宝藏女孩的你,不试一下吗?
|
运维 Serverless 开发者
重温童年记忆中的“五子棋” | 参与得证书+惊喜好礼
当童年记忆中的“五子棋”遇上最新最火热的 Serverless,会擦出什么样的火花?
重温童年记忆中的“五子棋” | 参与得证书+惊喜好礼
|
存储 JSON JavaScript
听说你情人节没人陪,这不我来给你介绍对象啦
听说你情人节没人陪,这不我来给你介绍对象啦
224 0
听说你情人节没人陪,这不我来给你介绍对象啦
|
算法 Java
快来,我悄悄的给你说几个HashCode的破事。 (1)
快来,我悄悄的给你说几个HashCode的破事。 (1)
118 0
快来,我悄悄的给你说几个HashCode的破事。 (1)
|
Java 测试技术
快来,我悄悄的给你说几个HashCode的破事。 (3)
快来,我悄悄的给你说几个HashCode的破事。 (3)
91 0
快来,我悄悄的给你说几个HashCode的破事。 (3)
|
测试技术
快来,我悄悄的给你说几个HashCode的破事。 (4)
快来,我悄悄的给你说几个HashCode的破事。 (4)
92 0
快来,我悄悄的给你说几个HashCode的破事。 (4)
|
Java
快来,我悄悄的给你说几个HashCode的破事。 (5)
快来,我悄悄的给你说几个HashCode的破事。 (5)
103 0
快来,我悄悄的给你说几个HashCode的破事。 (5)
|
API
大佬也hashcode方法上翻船了,不小心秀了一把!
大佬也hashcode方法上翻船了,不小心秀了一把!
143 0