散列的价值在于速度。我们使用数组来保存键的信息,这个信息并不是键本身,而是通过键对象生成一个数字(散列码),作为数组下标。由于数组的容量是固定的,而散列容器的大小是可变的,所以不同的键可以产生相同的数组下标(散列码)。也就是说,可能会有冲突(当然也有特例,比如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):散列与散列码的更多相关文章

  1. Java 集合系列 09 HashMap详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  2. Java 集合系列 10 Hashtable详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  3. Java 集合系列 06 Stack详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  4. Java 集合系列 05 Vector详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  5. Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  6. Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  7. Java 集合系列(四)—— ListIterator 源码分析

    以脑图的形式来展示Java集合知识,让零碎知识点形成体系 Iterator 对比   Iterator(迭代器)是一种设计模式,是一个对象,用于遍历集合中的所有元素.  Iterator 包含四个方法 ...

  8. Java集合框架——jdk 1.8 ArrayList 源码解析

    前言:作为菜鸟,需要经常回头巩固一下基础知识,今天看看 jdk 1.8 的源码,这里记录 ArrayList 的实现. 一.简介 ArrayList 是有序的集合: 底层采用数组实现对数据的增删查改: ...

  9. 7.Java集合-Arrays类实现原理及源码分析

    Java集合---Arrays类源码解析  转自:http://www.cnblogs.com/ITtangtang/p/3948765.html 一.Arrays.sort()数组排序 Java A ...

  10. java集合框架02——Collection架构与源码分析

    Collection是一个接口,它主要的两个分支是List和Set.如下图所示: List和Set都是接口,它们继承与Collection.List是有序的队列,可以用重复的元素:而Set是数学概念中 ...

随机推荐

  1. 牛客练习赛53 D 德育分博弈政治课 (思维建图,最大流)

    牛客练习赛53 D德育分博弈政治课 链接:https://ac.nowcoder.com/acm/contest/1114/D来源:牛客网 题目描述 德育分学长最近玩起了骰子.他玩的骰子不同,他的骰子 ...

  2. FFmpeg常用命令学习笔记(五)裁剪与合并命令

    裁剪与合并命令 1.音视频裁剪 ffmpeg -i input.mp4 -ss 00:01:00 -t 10 out.mp4 -ss:起始时间(HH:MM:SS).-t:裁剪时长(秒) 2.视频合并 ...

  3. Vue完成页面切换中加载数据

    created() { // 拿到路由传递来的car主键 let pk = this.$route.query.pk || this.$route.params.pk; // 主键不存在,就直接结束方 ...

  4. 洛谷P1417 烹调方案【dp】

    题目:https://www.luogu.org/problemnew/show/P1417 题意: 一道菜有$a,b,c$三个值.烧一道菜的时间是$c$.得到的价值是,$a-t*b$其中$t$是菜完 ...

  5. MFC 模态对话框、非模态对话框

    modal dialogs与modeless dialogs,modal dialogs会让原窗体不被选中,modeless dialogs原窗体依然可以选中. 1.模态对话框的显示 DlgModal ...

  6. centos6.5linux安装docker之升级内核

    一.运行docker Linux内核版本需要在3.8以上,针对centos6.5 内核为2.6的系统需要先升级内核.不然会特别卡 在yum的ELRepo源中,有mainline(4.5).long-t ...

  7. nextUntil([exp|ele][,fil]) 查找当前元素之后所有的同辈元素,直到遇到匹配的那个元素为止。

    nextUntil([exp|ele][,fil]) 概述 查找当前元素之后所有的同辈元素,直到遇到匹配的那个元素为止. 如果提供的jQuery代表了一组DOM元素,.nextUntil()方法也能让 ...

  8. PHP mysqli_multi_query() 函数

    实例 执行多个针对数据库的查询: <?php $con=mysqli_connect("localhost","my_user","my_pas ...

  9. Hibernate 4 升级到 Hibernate 5 的时候 SessionFactory 不能使用

    在 Hibernate 4 升级到 5 的时候老的 sessionFactory 出现错误. public static SessionFactory initSession() { Configur ...

  10. [Luogu] 花神游历各国

    https://www.luogu.org/problemnew/show/P4145 线段树区间求和 + 区间开根号 对1e9的数开根号下取整用不了几次就会<=1 因此暴力开根号,记录区间最大 ...