Java集合(7):散列与散列码
散列的价值在于速度。我们使用数组来保存键的信息,这个信息并不是键本身,而是通过键对象生成一个数字(散列码),作为数组下标。由于数组的容量是固定的,而散列容器的大小是可变的,所以不同的键可以产生相同的数组下标(散列码)。也就是说,可能会有冲突(当然也有特例,比如EnumMap和EnumSet)。所以,数组的值存放着一个保存所有相同散列码的值的list(引用)。然后对list中的值使用equals进行线性查询。如果散列函数设计的比较好的话,数组的每个位置只有较少的值,并且浪费空间也小。于是,查询过程就是首先计算键的散列码得到数组下标,然后内存寻址(时间复杂度为O(1),赋值)找到数组的值,再遍历list(时间复杂度为O(n),线性查询)即可。即hashCode和equals共同确定了对象的唯一性。
所有类都继承于Object。Object的hashCode方法生成的散列码,实际上是默认使用对象的地址计算散列码;而Object的equals方法实际上就是地址比较(==)。显然,当我们在使用散列容器(如HashMap的Key,HashSet等)时,我们自定义的类中还继承Object的hashCode和equals是不行的。必须重写hashCode和equals方法。好的hashCode()应该产生分布均匀的散列码。可以用IDE自动生成。下面是一个例子:
import java.util.List;
public class Test9 {
boolean a;
byte b;
short c;
int d;
char e;
long f;
float g;
double h;
String i;
List<String> j;
int[] k;
@Override
public int hashCode() {
// [STEP1] hashCode()里的魔法数字,之所以选择31,是因为它是个奇素数,如果乘数是偶数,并且乘法溢出的话,信息就会丢失,因为与2相乘等价于移位运算。
// 使用素数的好处并不是很明显,但是习惯上都使用素数来计算散列结果。31有个很好的特性,就是用移位和减法来代替乘法,可以得到更好的性能:31*i==(i<<5)-i。现在的VM可以自动完成这种优化。(from 《Effective Java》)
final int prime = 31;
// [STEP2] 为对象中每个有意义的域用下面公式计算散列码 result = prime * result + c
int result = 1;
// boolean
result = prime * result + (a ? 1231 : 1237);
// byte/short/int/char
result = prime * result + b;
result = prime * result + c;
result = prime * result + d;
result = prime * result + e;
// long
result = prime * result + (int) (f ^ (f >>> 32));
// float
result = prime * result + Float.floatToIntBits(g);
// double
long temp;
temp = Double.doubleToLongBits(h);
result = prime * result + (int) (temp ^ (temp >>> 32));
// 对象
result = prime * result + ((i == null) ? 0 : i.hashCode());
// List(要求每个元素实现hashCode)
result = prime * result + ((j == null) ? 0 : j.hashCode());
// 数组(要求每个元素实现hashCode)
result = prime * result + Arrays.hashCode(k);
// [STEP3] 返回
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Test9 other = (Test9) obj;
if (a != other.a)
return false;
if (b != other.b)
return false;
if (c != other.c)
return false;
if (d != other.d)
return false;
if (e != other.e)
return false;
if (f != other.f)
return false;
if (Float.floatToIntBits(g) != Float.floatToIntBits(other.g))
return false;
if (Double.doubleToLongBits(h) != Double.doubleToLongBits(other.h))
return false;
if (i == null) {
if (other.i != null)
return false;
} else if (!i.equals(other.i))
return false;
if (j == null) {
if (other.j != null)
return false;
} else if (!j.equals(other.j))
return false;
if (!Arrays.equals(k, other.k))
return false;
return true;
}
}
Java集合(7):散列与散列码的更多相关文章
- Java 集合系列 09 HashMap详细介绍(源码解析)和使用示例
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 10 Hashtable详细介绍(源码解析)和使用示例
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 06 Stack详细介绍(源码解析)和使用示例
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 05 Vector详细介绍(源码解析)和使用示例
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列(四)—— ListIterator 源码分析
以脑图的形式来展示Java集合知识,让零碎知识点形成体系 Iterator 对比 Iterator(迭代器)是一种设计模式,是一个对象,用于遍历集合中的所有元素. Iterator 包含四个方法 ...
- Java集合框架——jdk 1.8 ArrayList 源码解析
前言:作为菜鸟,需要经常回头巩固一下基础知识,今天看看 jdk 1.8 的源码,这里记录 ArrayList 的实现. 一.简介 ArrayList 是有序的集合: 底层采用数组实现对数据的增删查改: ...
- 7.Java集合-Arrays类实现原理及源码分析
Java集合---Arrays类源码解析 转自:http://www.cnblogs.com/ITtangtang/p/3948765.html 一.Arrays.sort()数组排序 Java A ...
- java集合框架02——Collection架构与源码分析
Collection是一个接口,它主要的两个分支是List和Set.如下图所示: List和Set都是接口,它们继承与Collection.List是有序的队列,可以用重复的元素:而Set是数学概念中 ...
随机推荐
- python 插件 Flask-装饰器 惰性载入视图
Flask 通常使用装饰器.装饰器简单易用,只要把 URL 放在相应的函数的前面就可以了. 但是这种方式有一个缺点:使用装饰器的代码必须预先导入,否则 Flask 就无法真正找到 你的函数. 当你必须 ...
- python pip 出现locations that require TLS/SSL异常处理方法
python pip 出现locations that require TLS/SSL异常处理方法 转载 郑才华 发布于2018-03-24 21:41:16 阅读数 51844 收藏 展开 最近在r ...
- [六省联考2017]分手是祝愿——期望DP
原题戳这里 首先可以确定的是最优策略一定是从大到小开始,遇到亮的就关掉,因此我们可以\(O(nlogn)\)的预处理出初始局面需要的最小操作次数\(tot\). 然后容(hen)易(nan)发现即使加 ...
- 中文录入问题 转载自:http://blog.csdn.net/ichsonx/article/details/8625925
2.1中文录入问题 默认安装的msysgit的shell环境中无法输入中文,为了能在shell界面输入中文,需要修改配置文件/etc/inputrc,增加或修改相关配置如下: #disable/ena ...
- 压测工具ab的简单使用
apache benchmark(ab)是一种常见的压测工具,不仅可以对apache进行压测,也可以对nginx,tomcat,IIS等进行压测 安装 如果安装了apache,那么ab已经自带了,不需 ...
- 删除3天前创建的以log结尾的文件
1. #/bin/bash # filename: del_log.sh find / -name "*.log" -mtime 3 | xargs rm -rf 2. #/bin ...
- Codeforces Round #402 (Div. 2) D题 【字符串二分答案+暴力】
D. String Game Little Nastya has a hobby, she likes to remove some letters from word, to obtain anot ...
- P5057 [CQOI2006]简单题 前缀异或差分/树状数组
好思路,好思路... 思路:前缀异或差分 提交:1次 题解:区间修改,单点查询,树状数组,如思路$qwq$ #include<cstdio> #include<iostream> ...
- 手把手教你在Linux系统下安装MySQL
在CentOS中默认安装有MariaDB,这个是MySQL的分支,但为了需要,还是要在系统中安装MySQL,而且安装完成之后可以直接覆盖掉MariaDB. 1. 下载并安装MySQL官方的 Yum R ...
- docker打包项目部署
1.首先在本地安装docker,步骤可参考https://www.cnblogs.com/conswin/p/11055853.html 2.在对应项目目录下增加Dockerfile文件,其内容可参考 ...