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是数学概念中 ...
随机推荐
- 牛客练习赛53 D 德育分博弈政治课 (思维建图,最大流)
牛客练习赛53 D德育分博弈政治课 链接:https://ac.nowcoder.com/acm/contest/1114/D来源:牛客网 题目描述 德育分学长最近玩起了骰子.他玩的骰子不同,他的骰子 ...
- FFmpeg常用命令学习笔记(五)裁剪与合并命令
裁剪与合并命令 1.音视频裁剪 ffmpeg -i input.mp4 -ss 00:01:00 -t 10 out.mp4 -ss:起始时间(HH:MM:SS).-t:裁剪时长(秒) 2.视频合并 ...
- Vue完成页面切换中加载数据
created() { // 拿到路由传递来的car主键 let pk = this.$route.query.pk || this.$route.params.pk; // 主键不存在,就直接结束方 ...
- 洛谷P1417 烹调方案【dp】
题目:https://www.luogu.org/problemnew/show/P1417 题意: 一道菜有$a,b,c$三个值.烧一道菜的时间是$c$.得到的价值是,$a-t*b$其中$t$是菜完 ...
- MFC 模态对话框、非模态对话框
modal dialogs与modeless dialogs,modal dialogs会让原窗体不被选中,modeless dialogs原窗体依然可以选中. 1.模态对话框的显示 DlgModal ...
- centos6.5linux安装docker之升级内核
一.运行docker Linux内核版本需要在3.8以上,针对centos6.5 内核为2.6的系统需要先升级内核.不然会特别卡 在yum的ELRepo源中,有mainline(4.5).long-t ...
- nextUntil([exp|ele][,fil]) 查找当前元素之后所有的同辈元素,直到遇到匹配的那个元素为止。
nextUntil([exp|ele][,fil]) 概述 查找当前元素之后所有的同辈元素,直到遇到匹配的那个元素为止. 如果提供的jQuery代表了一组DOM元素,.nextUntil()方法也能让 ...
- PHP mysqli_multi_query() 函数
实例 执行多个针对数据库的查询: <?php $con=mysqli_connect("localhost","my_user","my_pas ...
- Hibernate 4 升级到 Hibernate 5 的时候 SessionFactory 不能使用
在 Hibernate 4 升级到 5 的时候老的 sessionFactory 出现错误. public static SessionFactory initSession() { Configur ...
- [Luogu] 花神游历各国
https://www.luogu.org/problemnew/show/P4145 线段树区间求和 + 区间开根号 对1e9的数开根号下取整用不了几次就会<=1 因此暴力开根号,记录区间最大 ...