• lesson10:hashmap变慢原因分析


    下面的英文描述了String.hashCode()方法,在特定情况下,返回值为0的问题:

    Java offers the HashMap and Hashtable classes, which use the 
    String.hashCode() hash function. It is very similar to DJBX33A (instead of 33, it uses the 
    multiplication constant 31 and instead of the start value 5381 it uses 0). Thus it is also 
    vulnerable to an equivalent substring attack. When hashing a string, Java also caches the 
    hash value in the hash attribute, but only if the result is different from zero. 
    Thus, the target value zero is particularly interesting for an attacker as it prevents caching 
    and forces re-hashing. 

    接下来我们来看一下String类的hashCode()方法:当下面代码中的val[off++]返回值都是0的情况下,hashCode()的返回值也是0

        public int hashCode() {
            int h = hash;//初始值为0
            if (h == 0 && count > 0) {//count值为字符个数
                int off = offset;//off值为0
                char val[] = value;//字符数组
                int len = count;
    
                for (int i = 0; i < len; i++) {
                    h = 31*h + val[off++];//如果val[off++]的所有返回值都是ascii码0会发生什么?
                }
                hash = h;
            }
            return h;
        }
    

    我们知道hashmap存储值的数据结构是数组+链表的结果,如果不同的key值,但是返回的hashcode()值都是0的话,hashmap的结构不会得到很好的应用,会造成所有的元素都存储在数组的第一个元素的链表中,下面通过代码来证明:

    package com.mantu.advance;
    
    import java.util.HashMap;
    
    
    public class Lesson10HashmapLeak {
    
        public static void main(String[] args){
            testHashMapNormal();
            testHashMapBug();
        }
        
        public static void testHashMapBug(){
            HashMap<String,String> map = new HashMap<String,String>(100000);
            String xxx= asciiToString("0");
            String temp = xxx;
            long beginTime = System.currentTimeMillis();
            //System.out.println("开始时间:"+System.currentTimeMillis());
            for(int i=0;i<100000;i++){
                map.put(xxx, i+"");
    
                if((i%10000)==0){
                    xxx=temp;
                }
                else{
                    xxx=xxx+temp;
                }
            }
            System.out.println("testHashMapBug()耗时:"+(System.currentTimeMillis()-beginTime)+"毫秒");
        }
        
        public static void testHashMapNormal(){
            HashMap<String,String> map = new HashMap<String,String>(100000);
            String xxx= asciiToString("1");
            String temp = xxx;
            long beginTime = System.currentTimeMillis();
            //System.out.println("开始时间:"+System.currentTimeMillis());
            for(int i=0;i<100000;i++){
                map.put(xxx, i+"");
    
                if((i%10000)==0){
                    xxx=temp;
                }
                else{
                    xxx=xxx+temp;
                }
            }
            System.out.println("testHashMapNormal()耗时:"+(System.currentTimeMillis()-beginTime)+"毫秒");
        }
        public static String asciiToString(String value)
        {
            StringBuffer sbu = new StringBuffer();
            String[] chars = value.split(",");
            for (int i = 0; i < chars.length; i++) {
                sbu.append((char) Integer.parseInt(chars[i]));
            }
            return sbu.toString();
        }
    }

    最后的执行结果是:

    正常key值的一组执行时间是:1887毫秒

    key值对应的hashcode()值为0的执行时间是:7365毫秒

  • 相关阅读:
    css之选择器及性能优化
    css之font
    css之background
    Oracle 学习笔记(十)
    数据库脚本开发日志模板 —— 项目需求 A
    Java 程序动态替换 docx 模板中定制值的实现例子
    Quartz 定时器 Cron 表达式 怎么破?
    Tomcat 启动报错;ERROR: transport error 202: bind failed: Address already in use
    Oracle 学习笔记(九)
    Oracle 学习笔记(八)
  • 原文地址:https://www.cnblogs.com/mantu/p/6151891.html
Copyright © 2020-2023  润新知