java集合框架之HashCode
参考http://how2j.cn/k/collection/collection-hashcode/371.html
List查找的低效率
假设在List中存放着无重复名称,没有顺序的2000000个Hero
要把名字叫做“hero 1000000”的对象找出来
List的做法是对每一个进行挨个遍历,直到找到名字叫做“hero 1000000”的英雄。
最差的情况下,需要遍历和比较2000000次,才能找到对应的英雄。
测试逻辑:
1. 初始化2000000个对象到ArrayList中
2. 打乱容器中的数据顺序
3. 进行10次查询,统计每一次消耗的时间
不同计算机的配置情况下,所花的时间是有区别的。 在本机上,花掉的时间大概是600毫秒左右
package collection; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import charactor.Hero; public class TestCollection {
public static void main(String[] args) {
List<Hero> heros = new ArrayList<Hero>(); for (int j = ; j < ; j++) {
Hero h = new Hero("Hero " + j);
heros.add(h);
} // 进行10次查找,观察大体的平均值
for (int i = ; i < ; i++) {
// 打乱heros中元素的顺序
Collections.shuffle(heros); long start = System.currentTimeMillis(); String target = "Hero 1000000"; for (Hero hero : heros) {
if (hero.name.equals(target)) {
System.out.println("找到了 hero!" );
break;
}
}
long end = System.currentTimeMillis();
long elapsed = end - start;
System.out.println("一共花了:" + elapsed + " 毫秒");
} }
}
HashMap的性能表现
使用HashMap 做同样的查找
1. 初始化2000000个对象到HashMap中。
2. 进行10次查询
3. 统计每一次的查询消耗的时间
可以观察到,几乎不花时间,花费的时间在1毫秒以内
package collection; import java.util.HashMap; import charactor.Hero; public class TestCollection {
public static void main(String[] args) { HashMap<String,Hero> heroMap = new HashMap<String,Hero>();
for (int j = ; j < ; j++) {
Hero h = new Hero("Hero " + j);
heroMap.put(h.name, h);
}
System.out.println("数据准备完成"); for (int i = ; i < ; i++) {
long start = System.currentTimeMillis(); //查找名字是Hero 1000000的对象
Hero target = heroMap.get("Hero 1000000");
System.out.println("找到了 hero!" + target.name); long end = System.currentTimeMillis();
long elapsed = end - start;
System.out.println("一共花了:" + elapsed + " 毫秒");
} }
}
HashMap原理与字典
在展开HashMap原理的讲解之前,首先回忆一下大家初中和高中使用的汉英字典。
比如要找一个单词对应的中文意思,假设单词是Lengendary,首先在目录找到Lengendary在第 555页。
然后,翻到第555页,这页不只一个单词,但是量已经很少了,逐一比较,很快就定位目标单词Lengendary。
555相当于就是Lengendary对应的hashcode
分析HashMap性能卓越的原因
分析HashMap性能卓越的原因
-----hashcode概念-----
所有的对象,都有一个对应的hashcode(散列值)
比如字符串“gareen”对应的是1001 (实际上不是,这里是方便理解,假设的值)
比如字符串“temoo”对应的是1004
比如字符串“db”对应的是1008
比如字符串“annie”对应的也是1008
-----保存数据-----
准备一个数组,其长度是2000,并且设定特殊的hashcode算法,使得所有字符串对应的hashcode,都会落在0-1999之间
要存放名字是"gareen"的英雄,就把该英雄和名称组成一个键值对,存放在数组的1001这个位置上
要存放名字是"temoo"的英雄,就把该英雄存放在数组的1004这个位置上
要存放名字是"db"的英雄,就把该英雄存放在数组的1008这个位置上
要存放名字是"annie"的英雄,然而 "annie"的hashcode 1008对应的位置已经有db英雄了,那么就在这里创建一个链表,接在db英雄后面存放annie
-----查找数据-----
比如要查找gareen,首先计算"gareen"的hashcode是1001,根据1001这个下标,到数组中进行定位,(根据数组下标进行定位,是非常快速的) 发现1001这个位置就只有一个英雄,那么该英雄就是gareen.
比如要查找annie,首先计算"annie"的hashcode是1008,根据1008这个下标,到数组中进行定位,发现1008这个位置有两个英雄,那么就对两个英雄的名字进行逐一比较(equals),因为此时需要比较的量就已经少很多了,很快也就可以找出目标英雄
这就是使用hashmap进行查询,非常快原理。
这是一种用空间换时间的思维方式
HashSet判断是否重复
HashSet的数据是不能重复的,相同数据不能保存在一起,到底如何判断是否是重复的呢?
根据HashSet和HashMap的关系,我们了解到因为HashSet没有自身的实现,而是里面封装了一个HashMap,所以本质上就是判断HashMap的key是否重复。
再通过上一步的学习,key是否重复,是由两个步骤判断的:
hashcode是否一样
如果hashcode不一样,就是在不同的坑里,一定是不重复的
如果hashcode一样,就是在同一个坑里,还需要进行equals比较
如果equals一样,则是重复数据
如果equals不一样,则是不同数据。
自定义字符串的hashcode
如下是Java API提供的String的hashcode生成办法;
s[0] 表示第一位字符
n表示字符串的长度
本练习并不是要求去理解这个算法,而是自定义一个简单的hashcode算法,计算任意字符串的hashcode
因为String类不能被重写,所以我们通过一个静态方法来返回一个String的hashcode
如果字符串长度是0,则返回0。
否则: 获取每一位字符,转换成数字后,相加,最后乘以23
如果值超过了1999,则取2000的余数,保证落在0-1999之间。
如果是负数,则取绝对值。
随机生成长度是2-10的不等的100个字符串,打印用本hashcode获取的值分别是多少
HashCode代码
package Test.testtest; /**
* @Auther: 李景然
* @Date: 2018/5/24 22:48
* @Description:
*/
public class HashCode {
public static void main(String[] args) {
int i=;
while (i<){
double strLength=Math.ceil(Math.random()*+);
StringBuilder sb=new StringBuilder();
char c;
int start=(int)'';
int end=(int)'z'+; for (int j=;j<strLength;j++){
c=(char) (Math.random()*(end-start)+start);
if(Character.isLetterOrDigit(c)){
sb.append(c);
}else{
j--;
} }
System.out.println(sb.toString()+"---"+hashcode(sb.toString()));
i++;
}
} public static int hashcode(String str){
int result=;
if(str==null||str.length()==){
return ;
}
char[] chars=str.toCharArray();
for (char c :chars){
result+=(int)c;
}
result*=;
if(result>){
result%=;
}
return result;
}
}
部分结果:
自定义MyHashMap
根据前面学习的hashcode的原理和自定义hashcode, 设计一个MyHashMap,实现接口IHashMap
MyHashMap内部由一个长度是2000的对象数组实现。
设计put(String key,Object value)方法
首先通过上一个自定义字符串的hashcode练习获取到该字符串的hashcode,然后把这个hashcode作为下标,定位到数组的指定位置。
如果该位置没有数据,则把字符串和对象组合成键值对Entry,再创建一个LinkedList,把键值对,放进LinkedList中,最后把LinkedList 保存在这个位置。
如果该位置有数据,一定是一个LinkedList,则把字符串和对象组合成键值对Entry,插入到LinkedList后面。
设计 Object get(String key) 方法
首先通过上一个自定义字符串的hashcode练习获取到该字符串的hashcode,然后把这个hashcode作为下标,定位到数组的指定位置。
如果这个位置没有数据,则返回空
如果这个位置有数据,则挨个比较其中键值对的键-字符串,是否equals,找到匹配的,把键值对的值,返回出去。找不到匹配的,就返回空
IhashMap代码
package Test.testtest; /**
* @Auther: 李景然
* @Date: 2018/5/24 22:46
* @Description:
*/
public interface IHashMap {
public void put(String key,Object object);
public Object get(String key);
}
Entry代码
package Test.testtest; /**
* @Auther: 李景然
* @Date: 2018/5/24 22:46
* @Description:
*/
public class Entry { public Entry(Object key, Object value) {
super();
this.key = key;
this.value = value;
}
public Object key;
public Object value;
@Override
public String toString() {
return "[key=" + key + ", value=" + value + "]";
} }
MyHashMap代码
package Test.testtest; import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List; /**
* @Auther: 李景然
* @Date: 2018/5/24 22:47
* @Description:
*/
public class MyHashMap implements IHashMap {
private Object[] objects=new Object[]; public MyHashMap(){
} @Override
public void put(String key, Object object) {
int hashCode=HashCode.hashcode(key);
if(objects[hashCode]==null){
//key值相同的时候,把value存放到链表中
LinkedList<Entry> list=new LinkedList<>();
list.add(new Entry(key,object));
objects[hashCode]=list;
}else{
LinkedList<Entry> list=(LinkedList<Entry>)objects[hashCode];
list.add(new Entry(key,object));
objects[hashCode]=list;
}
} @Override
public Object get(String key) {
int hashCode=HashCode.hashcode(key);
Object object=objects[hashCode];
if(object!=null){
LinkedList<Entry> list=(LinkedList<Entry>)object;
for (Entry e:list){
if(e.key.equals(key)){
return e.value;
}
}
}
return null;
}
}
内容查找性能比较
重复前面的练习-查找内容性能比较 ,不过不使用HashMap,而是使用上个练习中自定义的MyHashMap.
准备一个ArrayList其中存放100000(十万个)Hero对象,其名称是随机的,格式是hero-[4位随机数]
hero-3229
hero-6232
hero-9365
...
因为总数很大,所以几乎每种都有重复,把名字叫做 hero-5555的所有对象找出来
要求使用两种办法来寻找
1. 不使用MyHashMap,直接使用for循环找出来,并统计花费的时间
2. 借助MyHashMap,找出结果,并统计花费的时间
MyHashMapTest代码
package Test.testtest; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; /**
* @Auther: 李景然
* @Date: 2018/5/24 23:04
* @Description:
*/
public class MyHashMapTest {
public static void main(String[] args) {
//初始化100000个英雄对象
ArrayList<Hero> arrayList=new ArrayList<>();
for (int i=;i<;i++){
String str="hero-"+String.valueOf(Math.random()*+).substring(,);
arrayList.add(new Hero(str));
} //初始化HashMap
HashMap<String,ArrayList<Hero>> hashMap=new HashMap<>();
for (Hero h:arrayList){
ArrayList<Hero> list=hashMap.get(h.getName());
if(list==null){
list=new ArrayList<>();
hashMap.put(h.getName(),list);
}
list.add(h);
} //初始化MyHashMap
MyHashMap myHashMap=new MyHashMap();
for (Hero h:arrayList){
ArrayList<Hero> list=(ArrayList<Hero>)(myHashMap.get(h.getName()));
if(list==null){
list=new ArrayList<>();
myHashMap.put(h.getName(),list);
}
list.add(h);
} searchOfHashMapTest(hashMap,"hero-5555");
searchOfMyHashMapTest(myHashMap,"hero-5555"); } //使用hashMap循环来找hero-5555
private static void searchOfHashMapTest(HashMap<String,ArrayList<Hero>> hashMap,String key){
double startTime=System.currentTimeMillis();
ArrayList<Hero> list=hashMap.get(key);
double endTime=System.currentTimeMillis();
if(list!=null){
System.out.println("使用hashMap循环来找hero-5555:找到"+list.size()+"个;用时:"+(endTime-startTime)+"毫秒");
}else{
System.out.println("使用hashMap循环来找hero-5555:找到"++"个;用时:"+(endTime-startTime)+"毫秒");
} } //使用myHashMap循环来找hero-5555
private static void searchOfMyHashMapTest(MyHashMap myHashMap,String key){
double startTime=System.currentTimeMillis();
int count=;
ArrayList<Hero> list=(ArrayList<Hero>) myHashMap.get(key);
double endTime=System.currentTimeMillis();
if(list!=null){
System.out.println("使用myHashMap循环来找hero-5555:找到"+list.size()+"个;用时:"+(endTime-startTime)+"毫秒");
}else{
System.out.println("使用myHashMap循环来找hero-5555:找到"++"个;用时:"+(endTime-startTime)+"毫秒");
} }
运行结果:
虽然用HashMap或者MyHashMap查询速度快,几乎不花费时间。但是要用HashMap或者MyHashMap,我们首先要把ArrayList中的数据转移到HashMap或者MyHashMap中,这个也需要消耗时间,降低性能(从这个中可以看出 练习-查找内容性能比较)
java集合框架之HashCode的更多相关文章
- Java集合框架List,Map,Set等全面介绍
Java集合框架的基本接口/类层次结构: java.util.Collection [I]+--java.util.List [I] +--java.util.ArrayList [C] +- ...
- 【集合框架】Java集合框架综述
一.前言 现笔者打算做关于Java集合框架的教程,具体是打算分析Java源码,因为平时在写程序的过程中用Java集合特别频繁,但是对于里面一些具体的原理还没有进行很好的梳理,所以拟从源码的角度去熟悉梳 ...
- Java 集合框架
Java集合框架大致可以分为五个部分:List列表,Set集合.Map映射.迭代器.工具类 List 接口通常表示一个列表(数组.队列.链表 栈),其中的元素 可以重复 的是:ArrayList 和L ...
- (转)Java集合框架:HashMap
来源:朱小厮 链接:http://blog.csdn.net/u013256816/article/details/50912762 Java集合框架概述 Java集合框架无论是在工作.学习.面试中都 ...
- Java集合框架(常用类) JCF
Java集合框架(常用类) JCF 为了实现某一目的或功能而预先设计好一系列封装好的具有继承关系或实现关系类的接口: 集合的由来: 特点:元素类型可以不同,集合长度可变,空间不固定: 管理集合类和接口 ...
- 浅谈JAVA集合框架
浅谈JAVA集合框架 Java提供了数种持有对象的方式,包括语言内置的Array,还有就是utilities中提供的容器类(container classes),又称群集类(collection cl ...
- 【JAVA集合框架之Map】
一.概述.1.Map是一种接口,在JAVA集合框架中是以一种非常重要的集合.2.Map一次添加一对元素,所以又称为“双列集合”(Collection一次添加一个元素,所以又称为“单列集合”)3.Map ...
- Java集合框架:HashMap
转载: Java集合框架:HashMap Java集合框架概述 Java集合框架无论是在工作.学习.面试中都会经常涉及到,相信各位也并不陌生,其强大也不用多说,博主最近翻阅java集合框架的源码以 ...
- java集合框架之java HashMap代码解析
java集合框架之java HashMap代码解析 文章Java集合框架综述后,具体集合类的代码,首先以既熟悉又陌生的HashMap开始. 源自http://www.codeceo.com/arti ...
随机推荐
- kubernetes集群管理之通过jq来截取属性
系列目录 首先要声明,这里的jq并不是批前端框架里的jquery,而是一个处理json的命令行工具. jq工具相比yq,它更加成熟,功能也更加强大,主要表现在以下几个方面 支持递归查找(我点对我们平时 ...
- AQS实现公平锁和非公平锁
https://www.cnblogs.com/chengdabelief/p/7493200.html AQS(AbstractQueuedSynchronizer类)是一个用来构建锁和同步器的框架 ...
- datatable的使用
学习可参考:http://www.guoxk.com/node/jquery-datatables http://yuemeiqing2008-163-com.iteye.com/blog/20069 ...
- 服务器启动时Webapp的web.xml中配置的加载顺序(转载)
一 1.启动一个WEB项目的时候,WEB容器会去读取它的配置文件web.xml,读取<listener>和<context-param>两个结点. 2.紧急着,容创建一个Ser ...
- linux命令详解:file命令
前言 file命令可以获取多种文件类型,包括文本文件.脚本文件.源码文件.多媒体文件(音频视频)等.file是通过查看文件的头部内容,来获取文件的类型,而不像Window那样是通过扩展名来确定文件类型 ...
- Hybrid--WebView中使用Ajax
Hybrid框架下的app,使用的Ajax.须要注意的是UIWebViewDelegate不会监測到Ajax的request.也就是再运行Ajax代码时.shouldStartLoadWithReuq ...
- EasyDarwin开源流媒体项目
本文转自EasyDarwin CSDN官方博客:http://blog.csdn.net/easydarwin EasyDarwin是由国内开源流媒体团队维护和迭代的一整套开源流媒体视频平台框架,从2 ...
- 今日头条Go建千亿级微服务的实践
今日头条Go建千亿级微服务的实践_36氪 http://36kr.com/p/5073181.html
- Hadoop实战-使用Eclipse开发Hadoop API程序(四)
一.准备运行所需Jar包 1)avro-1.7.4.jar 2)commons-cli-1.2.jar 3)commons-codec-1.4.jar 4)commons-collections-3. ...
- flex平分测试
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...