Java hashCode原理

步骤 1 : 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 = 0; j < 2000000; j++) {
Hero h = new Hero("Hero " + j);
heros.add(h);
} // 进行10次查找,观察大体的平均值
for (int i = 0; i < 10; 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 + " 毫秒");
} }
}

步骤 2 : 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 = 0; j < 2000000; j++) {
Hero h = new Hero("Hero " + j);
heroMap.put(h.name, h);
}
System.out.println("数据准备完成"); for (int i = 0; i < 10; 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 + " 毫秒");
} }
}

步骤 3 : HashMap原理与字典

在展开HashMap原理的讲解之前,首先回忆一下大家初中和高中使用的汉英字典。

比如要找一个单词对应的中文意思,假设单词是Lengendary,首先在目录找到Lengendary在第 555页。

然后,翻到第555页,这页不只一个单词,但是量已经很少了,逐一比较,很快就定位目标单词Lengendary。

555相当于就是Lengendary对应的hashcode

步骤 4 : 分析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进行查询,非常快原理。

这是一种用空间换时间的思维方式



步骤 5 : HashSet判断是否重复

HashSet的数据是不能重复的,相同数据不能保存在一起,到底如何判断是否是重复的呢?

根据HashSet和HashMap的关系,我们了解到因为HashSet没有自身的实现,而是里面封装了一个HashMap,所以本质上就是判断HashMap的key是否重复。

再通过上一步的学习,key是否重复,是由两个步骤判断的:

hashcode是否一样

如果hashcode不一样,就是在不同的坑里,一定是不重复的

如果hashcode一样,就是在同一个坑里,还需要进行equals比较

如果equals一样,则是重复数据

如果equals不一样,则是不同数据。

练习自定义字符串的hashcode

如下是Java API提供的String的hashcode生成办法;

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

s[0] 表示第一位字符

n表示字符串的长度

本练习并不是要求去理解这个算法,而是自定义一个简单的hashcode算法,计算任意字符串的hashcode

因为String类不能被重写,所以我们通过一个静态方法来返回一个String的hashcode

public static int hashcode(String)

如果字符串长度是0,则返回0。

否则: 获取每一位字符,转换成数字后,相加,最后乘以23

(s[0]+ s[1] + s[2] + s[3]+ s[n-1])*23.

如果值超过了1999,则取2000的余数,保证落在0-1999之间。

如果是负数,则取绝对值。

随机生成长度是2-10的不等的100个字符串,打印用本hashcode获取的值分别是多少

答案

package collection;

public class TestCollection {

    public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
int length = (int) (Math.random()*8+2);
String str = randomString(length);
int hashcode = hashcode(str);
System.out.printf("%-11s的自定义hashcode是:%d%n",str,hashcode);
} } private static int hashcode(String str) {
// TODO Auto-generated method stub
if(0==str.length())
return 0; int hashcode = 0;
char[]cs= str.toCharArray();
for (int i = 0; i < cs.length; i++) {
hashcode +=cs[i];
}
hashcode*=23;
//取绝对值
hashcode = hashcode<0?0-hashcode:hashcode;
//落在0-1999之间
hashcode %=2000; return hashcode;
} private static String randomString(int length) {
String pool = "";
for (short i = '0'; i <= '9'; i++) {
pool += (char) i;
}
for (short i = 'a'; i <= 'z'; i++) {
pool += (char) i;
}
for (short i = 'A'; i <= 'Z'; i++) {
pool += (char) i;
}
char cs[] = new char[length];
for (int i = 0; i < cs.length; i++) {
int index = (int) (Math.random() * pool.length());
cs[i] = pool.charAt(index);
}
String result = new String(cs);
return result;
} }

Java自学-集合框架 hashCode原理的更多相关文章

  1. Java自学-集合框架 HashSet

    Java集合框架 HashSet 示例 1 : 元素不能重复 Set中的元素,不能重复 package collection; import java.util.HashSet; public cla ...

  2. Java自学-集合框架 Collections

    Java集合框架 工具类Collections Collections是一个类,容器的工具类,就如同Arrays是数组的工具类 步骤 1 : 反转 reverse 使List中的数据发生翻转 pack ...

  3. Java自学-集合框架 Collection

    Java集合框架 Collection Collection是一个接口 步骤 1 : Collection Collection是 Set List Queue和 Deque的接口 Queue: 先进 ...

  4. Java自学-集合框架 HashMap

    Java集合框架 HashMap 示例 1 : HashMap的键值对 HashMap储存数据的方式是-- 键值对 package collection; import java.util.HashM ...

  5. Java自学-集合框架 二叉树

    Java集合框架 二叉树 示例 1 : 二叉树概念 二叉树由各种节点组成 二叉树特点: 每个节点都可以有左子节点,右子节点 每一个节点都有一个值 package collection; public ...

  6. Java自学-集合框架 LinkedList

    Java集合框架 LinkedList 序列分先进先出FIFO,先进后出FILO FIFO在Java中又叫Queue 队列 FILO在Java中又叫Stack 栈 示例 1 : LinkedList ...

  7. Java自学-集合框架 与数组的区别

    Java集合框架与数组的区别 示例 1 : 使用数组的局限性 如果要存放多个对象,可以使用数组,但是数组有局限性 比如 声明长度是10的数组 不用的数组就浪费了 超过10的个数,又放不下 //Test ...

  8. Java自学-集合框架 遍历

    遍历ArrayList的三种方法 步骤 1 : 用for循环遍历 通过前面的学习,知道了可以用size()和get()分别得到大小,和获取指定位置的元素,结合for循环就可以遍历出ArrayList的 ...

  9. Java自学-集合框架 ArrayList和HashSet的区别

    Java ArrayList和HashSet的区别 示例 1 : 是否有顺序 ArrayList: 有顺序 HashSet: 无顺序 HashSet的具体顺序,既不是按照插入顺序,也不是按照hashc ...

随机推荐

  1. PTA的Python练习题(五)

    昨天耽搁了一天,今天继续 从  第3章-5 字符转换 开始 1. a=input() b="" for i in a: ': b=b+iprint(int(b))#(写成b=i+b ...

  2. 汇编语言从入门到精通-5微机CPU的指令系统2

    微机CPU的指令系统 5.2.2 标志位操作指令 标志位操作指令是一组对标志位置位.复位.保存和恢复等操作的指令. 1.进位CF操作指令 a.清进位指令CLC(Clear Carry Flag):CF ...

  3. HDU1176免费馅饼(DP)

    都说天上不会掉馅饼,但有一天gameboy正走在回家的小径上,忽然天上掉下大把大把的馅饼.说来gameboy的人品实在是太好了,这馅饼别处都不掉,就掉落在他身旁的10米范围内.馅饼如果掉在了地上当然就 ...

  4. Python流程控制-2 条件判断

    条件判断 条件判断是通过一条或多条判断语句的执行结果(True或者False)来决定执行的代码块. 在Python语法中,使用if.elif和else三个关键字来进行条件判断. if语句的一般形式如下 ...

  5. 学习笔记(11)- 文本生成RNNLG

    https://github.com/shawnwun/RNNLG 数据集 给出了4个行业的语料,餐馆.酒店.电脑.电视,及其组合数据. 数据格式 任务 根据给定格式的命令,生成自然语言. 方法.模型 ...

  6. 简析ThreadLocal原理及应用

    简析ThreadLocal原理及应用 原创: 东晨雨 JAVA万维猿圈 4月17日 ThreadLocal的源码加上注释不超过八百行,源码结构清晰,代码也比较简洁.ThreadLocal可以说是Jav ...

  7. 任意promise串行执行算法 - 童彪

      // 任意promise串行执行算法 - 童彪 function runAllPromise() { var p1 = new Promise((resove, reject) => { s ...

  8. No module named 'PyQt5.QtWebEngineWidgets' 解决方法

    “No module named 'PyQt5.QtWebEngineWidgets”报错的原因是PyQt5版本过高,解决方法主要有两大类方法: [方法一] 指定安装5.10.1版本的pyqt5 pi ...

  9. vue iviem UI grid布局

    Grid 栅格 概述 我们采用了24栅格系统,将区域进行24等分,这样可以轻松应对大部分布局问题.使用栅格系统进行网页布局,可以使页面排版美观.舒适. 我们定义了两个概念,行row和列col,具体使用 ...

  10. keyup事件、keydown事件和input事件的区别

    keydown.keyup 属于键盘事件,input 属于文本事件 详细说明: keydown:当用户按下键盘上的任意按键时触发,如果按住不放,会重复触发此事件. keyup:当用户释放键盘上的按键时 ...