刚才debug追堆栈的时候发现一个很奇怪的问题

我用IE8和Google的浏览器访问同一个地址

Action的 scope="session" 也设置了

而且两个浏览器提交的参数map也是互相独立的

不过很奇怪的一个 两个Action对象的hashmap是不同的
但是它们的对象变量 paraterMap 的 哈希值 居然是一个

我大不解!
( 找到原因以后发现这个问题其实只是一个好多年没有再提起的基础问题 )
不过发现确实有朋友在网上说这个问题曾经引发过bug

Java中的hashCode()方法是一个比较特殊的地方
Object类中的hashCode是一个本地方法
public native int hashCode();
即使查看源码你也找不到它的实现, 因为它不是一个java编写的方法
此方法比较两个对象的内存地址是不是相等

6.1 覆写的HashCode方法
很多API中的子类会覆盖这个方法,最典型的应该是String类, 在String类中
   /**
     * Returns a hash code for this string. The hash code for a
     * <code>String</code> object is computed as
     * <blockquote><pre>
     * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
     * </pre></blockquote>
     * using <code>int</code> arithmetic, where <code>s[i]</code> is the
     * <i>i</i>th character of the string, <code>n</code> is the length of
     * the string, and <code>^</code> indicates exponentiation.
     * (The hash value of the empty string is zero.)
     *
     * @return a hash code value for this object.
     */
    public int hashCode() {
        int h = hash;
        if (h == 0) {
            int off = offset;
            char val[] = value;
            int len = count;

for (int i = 0; i < len; i++) {
                h = 31*h + val[off++];
            }
            hash = h;
        }
        return h;
}
可以看到, String类是使用它的 value值作为参数然后进行运算得出hashcode的
换句话说, 只要值相同的String不管是不是一个对象,hash值全部相等

也就是说运行下面的代码你会看见完全相同的hashcode
至于在不同的两台电脑上会不会相同我不知道,因为参与运算的off偏移量可能会不同

6.2 小心使用HashMap对象的hashcode
这是一个极为危险的行为!

public final int hashCode() {
            return (key==null ? 0 : key.hashCode()) ^
                   (value==null ? 0 : value.hashCode());
        }

在HashMap类中, 如果HashMap对象的内部对象是空的, 则hashcode 一定是 0
如果非空, 则遍历容器中的全部对象
然后取key的hashcode和value的hashcode 按位异或运算,然后把他们依次相加

也就是说, 如果你的两个HashMap的key和value全部相同的话, 那么, 它们的hashcode就是相同的

HashMap map1 = new HashMap();
                map1.put("test", "testValue");
                map1.put("test1", "testValue1");
                map1.put("test2", "testValue2");
                
                HashMap map2 = new HashMap();
                map2.put("test", "testValue");
                map2.put("test1", "testValue1");
                map2.put("test2", "testValue2");
                
                System.out.println(map1.hashCode());
                System.out.println(map2.hashCode());

下面代码的输出值是相等的

所以! 在任何时候! 不要使用HashMap或一些特殊容器的Hashcode作为key来进行缓存

以下内容转自
http://dingjob.javaeye.com/blog/696768
http://dingjob.javaeye.com/blog/696768
之所以写了两个连接,是因为douban上那个添加连接会携带 a href 锚点标签
我就是想看看如果我不用这个它会不会自动判断是链接地址,然后自己给加上
很满意, 豆瓣做的不脑残╮(╯_╰)╭

2010-06-22
Map的HashCode做缓存key值引发的重大bug
文章分类:Java编程
现象:

计费和账户的交互通过Map来交互,基本数据格式如下{"pp900_88",20,"pp900_61",2……}

在不同的取值情况下,较多数据返回了相同的价格结果,导致计算价格错误。

应用场景:

产品计算价格时,使用cache缓存了价格结果数据,cache的key值是传入map的hashCode,本意是要实现完全相同的Map传入值从缓存取数据,减少数据库的访问。

原因分析:

一。通过如下代码模拟线上应用:

Java代码
1.public static void main(String[] args) {
2. HashMap map = new HashMap();
3.
4. for(int i = 0 ; i < 100 ; i ++){
5.
6. HashMap m1 = new HashMap();
7.
8. m1.put("pp900_88", i);
9.
10. m1.put("pp900_59", 30);
11.
12. m1.put("pp900_62", 6);
13.
14. m1.put("pp900_63", 4);
15.
16. m1.put("pp900_60", "y");
17.
18. m1.put("pp900_61", i);
19.
20. int hs = m1.hashCode();
21.
22. map.put(hs, map.get(hs)+","+i);
23. // System.out.println(i+"==="+m1.hashCode());
24. }
25. System.out.println(map.size());
26. System.out.println(map);
27. }
  public static void main(String[] args) {
        HashMap map = new HashMap();

for(int i = 0 ; i < 100 ; i ++){

HashMap m1 = new HashMap();

m1.put("pp900_88", i);

m1.put("pp900_59", 30);

m1.put("pp900_62", 6);

m1.put("pp900_63", 4);

m1.put("pp900_60", "y");

m1.put("pp900_61", i);

int hs = m1.hashCode();

map.put(hs, map.get(hs)+","+i);
            // System.out.println(i+"==="+m1.hashCode());
        }
        System.out.println(map.size());
        System.out.println(map);
    } 结果如下:

Java代码
1.{-479160017=null,56,57,58,59,60,61,62,63,
2. -479160049=null,40,41,42,43,44,45,46,47,
3. -479160033=null,48,49,50,51,52,53,54,55,
4. -479160129=null,0,1,2,3,4,5,6,7,64,65,66,67,68,69,70,71,
5. -479160097=null,16,17,18,19,20,21,22,23,80,81,82,83,84,85,86,87,
6. -479160113=null,8,9,10,11,12,13,14,15,72,73,74,75,76,77,78,79,
7. -479160065=null,32,33,34,35,36,37,38,39,96,97,98,99,
8. -479160081=null,24,25,26,27,28,29,30,31,88,89,90,91,92,93,94,95}
{-479160017=null,56,57,58,59,60,61,62,63,
 -479160049=null,40,41,42,43,44,45,46,47,
 -479160033=null,48,49,50,51,52,53,54,55,
 -479160129=null,0,1,2,3,4,5,6,7,64,65,66,67,68,69,70,71,
 -479160097=null,16,17,18,19,20,21,22,23,80,81,82,83,84,85,86,87,
 -479160113=null,8,9,10,11,12,13,14,15,72,73,74,75,76,77,78,79,
 -479160065=null,32,33,34,35,36,37,38,39,96,97,98,99,
 -479160081=null,24,25,26,27,28,29,30,31,88,89,90,91,92,93,94,95}

可以看到,重复是很有规律的,连续7、8个数据都是重复的,当然在真实情况下重复概率不会这么高(因为Map的其他key-value值不太可能完全相同)

二。分析hashCode的产生

HashMap的hashCode根据key和value值来计算hashCode,最后将各个元素的hashCode值相加,即

Java代码
1.public final int hashCode() {
2. return (key==null ? 0 : key.hashCode()) ^
3. (value==null ? 0 : value.hashCode());
4. }
 public final int hashCode() {
            return (key==null ? 0 : key.hashCode()) ^
                   (value==null ? 0 : value.hashCode());
        }

以 m1.put("pp900_88", 1); m1.put("pp900_61", 1);和

m1.put("pp900_88", 2); m1.put("pp900_61", 2);为例子:得到的key-Value的hashCode值如下

key key的hashCode value的hashCode Map的hashCode(key^value)

pp900_88 -79859962 1 -79859961

pp900_61 -79860031 1 -79860032
  pp900_88 -79859962 2 -79859964
  pp900_61 -79860031 2 -79860029
 
显然根据Map的hash值算法, Map的hashCode相加:第一行+第二行=第三行+第四行,这样产生重复数据也在所难免了,因为hashCode本来就不保证不同的输入值不会产生相同的结果。JSL的约束是对于相同的对象,必须产生相同的hashCode。
结论和改进措施:
1.不建议对结果进行缓存,结果缓存会带来很多问题,比如哪些数据变更需要刷新哪些缓存,缓存最好对原始纪录值进行缓存。
2.key值不采用hashCode算法,直接改为使用各个key-value的String拼接字符串。

千万不要误用 java 中的 HashCode 方法的更多相关文章

  1. 浅谈Java中的hashcode方法

    哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: 1 public native int hashCode(); 根据 ...

  2. 【转】浅谈Java中的hashcode方法(这个demo可以多看看)

    浅谈Java中的hashcode方法 哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native i ...

  3. 【转】浅谈Java中的hashcode方法

    哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native int hashCode(); 根据这个 ...

  4. Java中的hashcode方法

    一.hashCode方法的作用 对于包含容器类型的程序设计语言来说,基本上都会涉及到hashCode.在Java中也一样,hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列 ...

  5. 浅谈Java中的hashcode方法(转)

    原文链接:http://www.cnblogs.com/dolphin0520/p/3681042.html 浅谈Java中的hashcode方法 哈希表这个数据结构想必大多数人都不陌生,而且在很多地 ...

  6. 浅谈Java中的hashcode方法(转载)

    哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: 1 public native int hashCode(); 根据 ...

  7. 彻底理解Java中的hashcode方法(转)

    本文转自http://www.importnew.com/18851.html 哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有 ...

  8. 浅谈Java中的hashcode方法以及equals方法

    哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native int hashCode(); 根据这个 ...

  9. java中的hashcode方法作用以及内存泄漏问题

    本文装载:http://hi.baidu.com/iduany/item/6d66dfc9d5f2da1650505870 hashCode()方法的作用&使用分析 一直以来都想写篇文章来说明 ...

随机推荐

  1. POJ 3974 Palindrome

    D - Palindrome Time Limit:15000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Sub ...

  2. 关于RACK的一点简单介绍

    0 前言 如有错误欢迎指出,如需转载,请注明原文链接. 1 Rack是什么 一句话介绍,Rack是一个Web接口,定义了一系列的标准,具体实现的工作是交给服务器(puma, thin等)做的. 如果你 ...

  3. WP老杨解迷:如何获得更多的应用评价和解读内容刷新

    Windows Phone的市场评论功能研究的时间比较长,只是这一功能,估计就能写一篇论文,我曾搞过多款评论数超高的游戏,其中<少林塔防>是重量级的作品,至今稳坐最高评分第一把交椅,如果不 ...

  4. 如何下载Spring

    一. 各个spring版本的下载地址: http://repo.spring.io/release/org/springframework/spring 二. Spring官网: http://spr ...

  5. 关于php析构函数的一个有趣问题

    随着面向对象编程的普遍展开,面向对象展现了其中很多有趣的问题.相信很多初学者学习php面向对象时会接触两个函数,构造函数与析构函数.构造函数似乎用的更多,析构函数用的较少(相对初学者有限编程经验而言, ...

  6. 分布式人工智能标记语言(DAIML)示例

          DAIML(Distributed Artificial Intelligence Markup Language)是用于分布式人工智能系统中智能语言的标记库.DAIML主要分为Patte ...

  7. EntityFramework中的线程安全,又是Dictionary

    继上次记一次w3wp占用CPU过高的解决过程(Dictionary和线程安全)后又再次与Dictionary博弈,这一次是在EntityFramework中的Dictionary. 从一个异常说起 这 ...

  8. 如何添加win10命令提示符字体,美化显示效果

    如图,win10命令提示符的字体真让人难受的要死,难道就不能改成好看一点的吗?笔者开始了停不下来的百度和Google 在查找了大量零碎或者过时的资料后,终于找到了一篇看起来还是很靠谱的文章: Quic ...

  9. am,pm时间转换

    写在前面 最近遇到的一个问题,在英文操作系统上,获取到的时间是带am或者pm的.但是数据库是datetime类型,存储的时候竟然都变成0000-00-00 00:00:00.但是在中文操作系统上又是正 ...

  10. 每天一个linux命令(22):chgrp命令

    在 lunix系统里,文件或目录的权限的掌控以拥有者及所诉群组来管理.可以使用chgrp指令取变更文件与目录所属群组,这种方式采用群组名称或群组识别 码都可以.Chgrp命令就是change grou ...