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是数学概念中 ...
随机推荐
- FFmpeg常用命令学习笔记(四)处理原始数据命令
处理原始数据命令 通过音视频设备采集的.没有经过任何加工的数据叫原始数据,而像我们平时播放的比如mp4文件是压缩后的数据.视频原始数据是YUV格式,音频原始数据是PCM格式.FFmpeg可以从封装格 ...
- python_函数参数
1.参数的基本知识 任意个数 任意类型 def func(a1,a2,a3): print(a1,a2,a3) # 参数可以是任意个数和任意类型 func(1,'waf',True) 2.位置传参数( ...
- Redis持久化(三)
Redis持久化 Redis提供了哪些持久化机制 1. RDB持久化: 该机制是指在指定的时间间隔内将内存中的数据集快照写入磁盘. 2. AOF持久化: 该机制 ...
- CF776D The Door Problem[2-SAT]
翻译 对于一扇门,如果是关的,那么他必须使用其中一个开关开开来,如果是开的,要么使用两个开关,要么啥都不做.这样,每扇门恰好对应两种状态,要选一个. 考虑用2-SAT模型解决.连边的话是对于一个机关, ...
- 04 DRF内容回顾、用户登录 (含跨域) (vuex vue-cookie)、用户认证 (Auth认证)(拦截器)
1.内容回顾 1.视图中常见的继承 2.频率访问控制源码 3.序列化,反序列化 2.初始化代码 1.后端代码:AuthView (1)目录结构 (2)urls (3)view (4)注释掉cors ( ...
- Codeforces Round #346 (Div. 2) E题 并查集找环
E. New Reform Berland has n cities connected by m bidirectional roads. No road connects a city to it ...
- 收藏一个RMQ模板
int a[1100]; int dp[maxn][20]; void rmq_init(){ for(int i=0;i<n;i++) dp[i][0]=a[i]; for(int j=1;( ...
- Neo4j 简介 2019
Neo4j是一个世界领先的开源图形数据库,由 Java 编写.图形数据库也就意味着它的数据并非保存在表或集合中,而是保存为节点以及节点之间的关系. Neo4j 的数据由下面几部分构成: 节点边属性Ne ...
- K - Kia's Calculation(贪心)
Kia's Calculation Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others ...
- 5.rabbitmq--通配符模式Topics
rabbitmq--通配符模式Topics topic模式也称为主题模式,其实他相对于routing模式最大的好处就是他多了一种匹配模式的路由,怎么理解匹配呢,其实就相当于我们之前正则的.*这种,不过 ...