HashMap在Android和Java中的不同实现
本篇文章转载他人,原文地址:http://www.xyczero.com/blog/article/16/
起因
今天在项目中遇到一个很”奇葩”的问题。情况大致是这样的:Android终端和服务器(Spring),完全相同的字符串键值对放入 HashMap中竟然顺序不一样,这直接导致了服务器和Android终端用HmacSHA256算法加密出的摘要也不一样,服务器也就无法进行正确的数 据验证。
然后带着郁闷的心情给程序加断点进行原因寻找,发现原来是HashMap的中服务器和终端双方对于同样的key存放顺序竟然不一样!
在HashCode产生冲突的情况下,不同的key在HashMap中存入的位置应该是相同的,即使在hashCode产生冲入,如果key-value put的顺序相同,其存放的位置也应该是相同的。
寻找,解决
所以问题就应该出在HashMap上,只能去查看Java和Android关于HashMap的源码了,发现两者的hashCode()方法竟然不 一样,小小激动了一下,可仔细一看,发现Android只是优化Java中的hashCode()方法,使其更加易于阅读而已,但所运用的原理还是一样 的,真是=。=。
具体代码比较如下:
<!-- Android -->
@Override public int hashCode() {
int hash = hashCode;
if (hash == 0) {
if (count == 0) {
return 0;
}
final int end = count + offset;
final char[] chars = value;
for (int i = offset; i < end; ++i) {
hash = 31*hash + chars[i];
}
hashCode = hash;
}
return hash;
}
<!-- Java-->
public int hashCode() {
int h = hash;
int len = count;
if (h == 0 && len > 0) {
int off = offset;
char val[] = value;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
无奈,我只能继续在源码里查看比较,最后发现原来是两者的默认构造函数不一样,本质上就是两者的table大小不一样,Java中的table默认 大小是16*0.75=12(容量 * 负载因子),而Android中table的默认大小是2,所以即使是同样的字符串按同样的顺序放入HashMap中它们的key值存放顺序也会不一样。
<!-- Android -->
private static final Entry[] EMPTY_TABLE
= new HashMapEntry[MINIMUM_CAPACITY >>> 1];
//默认构造函数
public HashMap() {
table = (HashMapEntry<K, V>[]) EMPTY_TABLE;
threshold = -1; // Forces first put invocation to replace EMPTY_TABLE
}
<!-- Java -->
static final int DEFAULT_INITIAL_CAPACITY = 16;
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//默认构造函数
public HashMap() {
this(DEFAULT_INITIAL_CAPACITY,DEFAULT_LOAD_FACTOR);
}
其实仔细读源码会发现,在Android中所实现的HashMap类关于”阈值(threshold )”的设定也已经和Java不同了,具体请看截取的源码:
<!-- Android -->
//阈值固定取其table大小的3/4
threshold = (newCapacity >> 1) + (newCapacity >> 2);
<!-- Java -->
//阈值取容量*负载因子或最大容量+1间的小值
threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
小结
所以总结来看HashMap在不同平台或不同语言中的实现细节是不一样的,吃一堑,长一智,反正以后切记,牵扯到顺序时HashMap真的不适合!
相关文章
- java虚拟机学习-Java常量池理解与总结(13-2)
- 看雪『Android安全』板块 2018 年优秀和精华帖分类索引
- Android系统自带的android.util.Base64的实现源码
- Android面试题3之描写叙述下Android的系统架构
- Android Design Support Library(二)用NavigationView实现抽屉菜单界面
- Android中Webview使用javascript调用事先定义好的Java函数
- Java FutureTask<V> 源码分析 Android上的实现
- 《Java和Android开发学习指南(第2版)》——第2章,第2.4节变量
- 《Android平板电脑开发秘籍》——3.1节技巧:理解菜单和ActionBar之间的区别
- 实现Android Studio JNI开发C/C++使用__android_log_print输出Log
- Android使用JNI实现Java与C之间传递数据
- 基于 Android NDK 的学习之旅----- C调用Java
- android中获取 bitmap 像素的颜色 之吸管取色功能
- Android系统原理与源码分析(1):利用Java反射技术阻止通过按钮关闭对话框
- Android代码实现控件闪烁效果
- Android实现红绿灯检测(含Android源码 可实时运行)
- JAVA_用Java来获取访问者真实的IP地址
- Android N(API level 24.)废弃了Html.fromHtml(String)
- iOS及Android自动化实践
- 关于Android 构建
- 关于OKHTTP和RX及其他三方包冲突的问题,Error: Program type already present: rx.android.BuildConfig
- 【Java I/O 流】文件的操作——java.io.File 类详解
- Android 在Java代码中设置style属性,以ProgressBar为例