千万不要误用 java 中的 HashCode 方法
刚才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 方法的更多相关文章
- 浅谈Java中的hashcode方法
哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: 1 public native int hashCode(); 根据 ...
- 【转】浅谈Java中的hashcode方法(这个demo可以多看看)
浅谈Java中的hashcode方法 哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native i ...
- 【转】浅谈Java中的hashcode方法
哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native int hashCode(); 根据这个 ...
- Java中的hashcode方法
一.hashCode方法的作用 对于包含容器类型的程序设计语言来说,基本上都会涉及到hashCode.在Java中也一样,hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列 ...
- 浅谈Java中的hashcode方法(转)
原文链接:http://www.cnblogs.com/dolphin0520/p/3681042.html 浅谈Java中的hashcode方法 哈希表这个数据结构想必大多数人都不陌生,而且在很多地 ...
- 浅谈Java中的hashcode方法(转载)
哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: 1 public native int hashCode(); 根据 ...
- 彻底理解Java中的hashcode方法(转)
本文转自http://www.importnew.com/18851.html 哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有 ...
- 浅谈Java中的hashcode方法以及equals方法
哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native int hashCode(); 根据这个 ...
- java中的hashcode方法作用以及内存泄漏问题
本文装载:http://hi.baidu.com/iduany/item/6d66dfc9d5f2da1650505870 hashCode()方法的作用&使用分析 一直以来都想写篇文章来说明 ...
随机推荐
- zepto.js 源码解析
http://www.runoob.com/w3cnote/zepto-js-source-analysis.html Zepto是一个轻量级的针对现代高级浏览器的JavaScript库, 它与jqu ...
- C++ map的使用
C++ map的基本操作和使用 来源:(http://blog.sina.com.cn/s/blog_61533c9b0100fa7w.html) - C++ map的基本操作和使用_Live_新浪博 ...
- MySQL基础 - 编码设置
刚开始工作的时候,在工作中遇到的与数据库相关的问题,多半跟编码有关,总结一下数据库编码的设置. 字符集 查看数据库支持的字符集: mysql> SHOW CHARACTER SET; 从图中可以 ...
- Oracle PL/SQL中如何使用%TYPE和%ROWTYPE
1. 使用%TYPE 在许多情况下,PL/SQL变量可以用来存储在数据库表中的数据.在这种情况下,变量应该拥有与表列相同的类型.例如,students表的first_name列的类型为VARCHAR2 ...
- 为什么需要DTO(数据传输对象)
DTO即数据传输对象.之前不明白有些框架中为什么要专门定义DTO来绑定表现层中的数据,为什么不能直接用实体模型呢,有了DTO同时还要维护DTO与Model之间的映射关系,多麻烦. 然后看了这篇文章中的 ...
- 利用opencv3中的kmeans实现抠图功能
kmeans算法主要用来实现自动聚类,是一种非监督的机器学习算法,使用非常广泛.在opencv3.0中提供了这样一个函数,直接调用就能实现自动聚类,非常方便. 函数原型: C++: double km ...
- Ubuntu Navicat正版永久使用方法
最近技安不再带自己的mac book来公司工作了,公司多出来一个台式机,配置还挺高.于是乎就拿过来用了. 装上了Ubuntu14.04 LTS版,正常的开发工具如vagrant,vitualbox,s ...
- LeetCode 笔记28 Maximum Gap
Given an unsorted array, find the maximum difference between the successive elements in its sorted f ...
- OpenCV Start
开始学习opencv了. 从官网下载了 opencv-3.0.0-alpha.exe(windows版本) opencv-3.0.0-alpha.zip (linux版本) 从windows版本的安装 ...
- android加固签名工具(源码下载)
背景 每次android加固了都要命令行签名好麻烦,正好之前做了个图标生成工具. 所以改了改,比写批处理还要省事. 原理 其实就是用winform程序调用控制台执行命令,android签名的命令如下 ...