容器深入研究 --- 散列与散列码(三)

如何覆盖hashCode():

明白了如何散列之后,编写自己的hashCode()就更有意义了。

首先,你无法控制bucket数组的下标值的产生。这个值依赖于具体的HashMap对象的容量,而容量的改变与容器的充满程度和负载因子有关。hashCode()生成的结果,经过处理后称为桶位的下标。

设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该产生同样的值。如果在将一个对象用put()添加进HashMap时产生一个hashCode()值,而用get()去除时却产生了另外一个hashCode()值,那么就无法重新取得该对象了(也会造成内存泄露)。

所以,如果你的hashCode()方法依赖于对象中易变的数据,用户就要当心了,因为数据发生变化时,hashCode()就会生成一个不同的散列码,相当于产生了一个不同的键。

此外,也不应该使hashCode()依赖于具有唯一性的对象信息,尤其是使用this的值,这只能产生很糟糕的hashCode()。因为这样做无法生成一个新的键,使之与put()中原始的键值对中的键相同。

下面以String类为例。String有个特点:如果程序中有多个String对象,都包含相同的字符串序列,那么这些String对象都映射到同一块内存区域。所以new String("hello");生成的两个实例,虽然是相互独立的,但是对它们使用hashCode()应该生成同样的结果。

public static void main(String[] args) {
		String[] hellos = "Hello Hello".split(" ");
		System.out.println(hellos[0].hashCode());
		System.out.println(hellos[1].hashCode());
		//69609650
		//69609650
}

对于String而言,hashCode()明显是基于String内容的。

因此,要想使hashCode()实用,它必须速度快,并且必须有意义。也就是说必须基于对象的内容生成散列码。记得吗,散列码不必是独一无二的(应该更关注生成速度,而不是一致性),但是通过hashCode()和equals(),必须能够完全确定对象的身份。

因为在生成桶的下标前,hashCode()还需要做进一步的处理,所以散列码的生成范围并不重要,只要是int即可。

还有另一个因素影响:好的hashCode()应该产生均匀分布的散列码。如果散列码都集中在一块,那么HashMap或者HashSet在某些区域的负载会很重,这样就不如分布均匀的散列函数快。

写出一个像样的hashCode()的基本指导:

1,给int变量result赋予某个非零值常量。

2,为对象内每个意义的域f计算出一个int散列码c。

* -------------------------------------------------------------
 * 域类型		计算
 * boolean		c = (f ? 0 : 1 );
 * byte/char/short/int c = (int)f;
 * long 		c = (int)(f ^ f>>>32)
 * float 		c = Float.floatToInBits(f);
 * double		long l = Double.doubleToLongBits(f); c = (int)(l ^ (l>>>32));
 * Object		c = f.hashCode();
 * 数组			对每个元素应用应用以上规则

3,合并计算得到的散列码:result = 37 * result + c;

4,返回result

5,检查hashCode()最后生成的结果,确保相同的对象有相同的散列码。

例如:

public int hashCode() {
	int reuslt = 17;
	result = 37 * result + s.hashCode();
	result = 37 * result + (int)id;
	return result;
}

容器深入研究 --- 散列与散列码(三),布布扣,bubuko.com

时间: 2024-10-11 15:01:57

容器深入研究 --- 散列与散列码(三)的相关文章

容器深入研究 --- 散列与散列码(一)

通常的: 当标准类库中的类被作用HashMap的键.它用的很好,因为它具备了键所需的全部性质. 当你自己创建用作HashMap的键的类,有可能会忘记在其中放置必须的方法,而这时通常会犯的一个错误. 例如:考虑一个天气系统,将Groundhog对象与Prediction对象联系起来. class Groundhog { protected int number; public Groundhog(int n) { number = n; } public String toString() { r

容器深入研究 --- 散列与散列码(二)

为速度而散列: SlowMap.java说明了创建一个新的Map并不困难.但正如它的名称SlowMap所示,它不会很快,如果有更好的选择就应该放弃它.它的问题在于对键的查询,键没有按照任何特定的顺序保存,所以只能使用简单的线性查询,而线性查询是最慢的查询方式. 散列的价值在于速度: 散列使得查询得以快速进行.由于瓶颈在于键的查询速度,因此解决方案之一就是保持键的排序状态,然后使用Collections.binarySearch()进行查询. 散列则更进一步,它将键保存在某处,以便能够很快的找到.

Java散列和散列码的实现

转自:https://blog.csdn.net/al_assad/article/details/52989525 散列和散列码 ※正确的equals方法应该满足的的条件: ①自反性:x.equals(x) 一定返回true: ②对称性:y.euqlas(x)为true,那么x.equals(y)一定为true: ③传递性:x.equals(y)为true,y.euqlas(z)为true,则z.equals(x)为true: ④一致性:如果x,y中用于等价比较的信息没有变化,那么无论调用y.

java 散列与散列码探讨 ,简单HashMap实现散列映射表运行各种操作示列

package org.rui.collection2.maps; /** * 散列与散列码 * 将土拔鼠对象与预报对象联系起来, * @author lenovo * */ //土拨鼠 public class Groundhog { protected int number; public Groundhog(int n) { number=n; } @Override public String toString() { return "Groundhog #" + number

java 散列与散列码探讨 ,简单HashMap实现散列映射表执行各种操作示列

package org.rui.collection2.maps; /** * 散列与散列码 * 将土拔鼠对象与预报对象联系起来, * @author lenovo * */ //土拨鼠 public class Groundhog { protected int number; public Groundhog(int n) { number=n; } @Override public String toString() { return "Groundhog #" + number

【ThinkingInJava】51、散列与散列码

/** * 书本:<Thinking In Java> * 功能:散列与散列码 * 文件:Groundhog.java * 时间:2015年5月3日09:42:54 * 作者:cutter_point */ package Lesson17Containers; public class Groundhog { protected int number; //保护类型,继承之后还是保护类型 public Groundhog(int n) { number = n; } public Strin

数据结构--散列排序--散列表

散列表 散列查找,我们又回到了查找, 编译的时候,涉及变量及属性的管理: 插入:新变量的定义 查找:变量的引用 实际上是动态查找问题,查找树AVL树. 两个变量名(字符串)比较效率不高.字符串的比较要一个一个的比下去,时间会比较长, 是否可以把字符串转换成数字,再处理,就快多了.就是散列查找的思想. 已知的查找方法: 顺序查找                                          O(N) 二分查找(静态查找,不适合动态查找)   O(log2N) 二叉搜索数    

字典:散列表、散列字典、关键字列表、集合与结构体

字典 散列表和散列字典都实现了Dict的行为.Keyword模块也基本实现了,不同之处在于它支持重复键. Eunm.into可以将一种类型的收集映射转化成另一种. defmodule Sum do def values(dict) do dict |> Dict.values |> Enum.sum end end hd = [ one: 1, two: 2, three: 3 ] |> Enum.into HashDict.new IO.puts Sum.values(hd) #=&g

容器深入研究 --- 理解Map

通常的: 映射表(也称关联数组)的基本思想是它维护的键-值(对)关联,因此你可以使用键来查找值. 标准的Java类库中包含了Map的几种实现,包括:HashMap,TreeMap,LinkedHashMap,WeakHashMap,ConcurrentHashMap,IdentityHashMap. 它们都有同样的基本接口Map,但是行为特性各不相同,这表现在效率.键值对的保存及呈现次序.对象的保存周期.映射表如何在多线程程序中工作的判定"键"等价的策略方面.Map接口实现的数量应该让