在阅读StackOverflow上有关覆盖的所有问题和答案之后,GetHashCode()我编写了以下扩展方法,以便轻松方便地覆盖GetHashCode():
public static class ObjectExtensions
{
private const int _seedPrimeNumber = 691;
private const int _fieldPrimeNumber = 397;
public static int GetHashCodeFromFields(this object obj, params object[] fields) {
unchecked { //unchecked to prevent throwing overflow exception
int hashCode = _seedPrimeNumber;
for (int i = 0; i < fields.Length; i++)
if (fields[i] != null)
hashCode *= _fieldPrimeNumber + fields[i].GetHashCode();
return hashCode;
}
}
}
Run Code Online (Sandbox Code Playgroud)
(我基本上只重构了有人在那里发布的代码,因为我真的很喜欢它可以一般使用)
我用的是这样的:
public override int GetHashCode() {
return this.GetHashCodeFromFields(field1, field2, field3);
}
Run Code Online (Sandbox Code Playgroud)
你看到这段代码有什么问题吗?
将对象添加到.NET System.Collections.Generic.Dictionary类时,密钥的哈希码将在内部存储并用于以后的比较.当哈希码在其初始插入字典后发生变化时,它经常变得"不可访问",并且当存在检查(即使使用相同的引用)返回false时,它可能会使其用户感到惊讶(下面的示例代码).
该GetHashCode的文件说:
为对象GetHashCode方法必须一致,只要没有修改确定的该对象的Equals方法返回值的对象的状态返回相同的散列码.
因此,根据GetHashCode文档,只要更改了相等的确定状态,哈希码就会改变,但Dictionary实现不支持这一点.
当前的.NET字典实现是否被打破,因为它错误地忽略了哈希码限额?应该GetHashCode()只基于不可变成员?或者,还有什么可以打破可能的错误二分法吗?
class Hashable
{
public int PK { get; set; }
public override int GetHashCode()
{
if (PK != 0) return PK.GetHashCode();
return base.GetHashCode();
}
public override bool Equals(object obj)
{
return Equals(obj as Hashable);
}
public virtual bool Equals(Hashable other)
{
if (other == null) return false;
else if (ReferenceEquals(this, other)) return true;
else if (PK != 0 && other.PK != 0) return …Run Code Online (Sandbox Code Playgroud) 我昨天查看了一些.net源代码,看到了GetHashcode的几个实现,其中包含以下内容:
(i1 << 5) + i ^ i2
Run Code Online (Sandbox Code Playgroud)
我理解代码在做什么以及为什么.我想知道的是为什么他们使用(i1 << 5)+ i代替(i1 << 5)- i.
我见过的大多数框架都使用-i,因为它相当于乘以31这是素数,但是微软的方式相当于乘以33,它有11和3作为因子,因此不是素数.
这有什么理由吗?有任何合理的假设吗?
Java 8为String键提供备用散列,以在遇到大量密钥散列码冲突时提高性能.任何人都可以解释它是什么以及它将如何工作?
我已经阅读了过去几个小时的哈希码函数,并且在自定义哈希码实现中使用素数作为乘数已经积累了一些问题.如果我能对以下问题有所了解,我将不胜感激:
在对@ mattb的答案的评论中,@ hstoerr主张使用更大的素数(例如524287)而不是公共素数31.我的问题是,给定一对或元素的哈希码函数的以下实现:
@Override
public int hashCode() {
final int prime = 31;
int hash1 = (pg1 == null) ? 0 : pg1.hashCode();
int hash2 = (pg2 == null) ? 0 : pg2.hashCode();
return prime * (hash1 ^ hash2);
}
Run Code Online (Sandbox Code Playgroud)这不会导致返回的溢出,int如果prime是大数?
假设溢出是没有问题的(JVM做一个自动施法)是它更好地做一个位位移,而不是一个演员?
我认为哈希码函数的性能根据哈希码的复杂性而有很大差异.主乘数的大小是否不影响性能?
在自定义哈希码函数中使用多个素数而不是单个乘法器更好/更智能/更快?如果没有,还有其他一些优势吗?请参阅@ jinguy对相关问题的回答中的示例:
public int hashCode() {
return a * 13 + b.hashCode() * 23 + (c? 31: 7);
}
Run Code Online (Sandbox Code Playgroud)其中a是一个int,b是一个 …
读完这个问题后为什么"int"和"sbyte"GetHashCode函数生成不同的值?我想进一步挖掘并发现以下行为:
sbyte i = 1;
int j = 1;
object.Equals(i, j) //false (1)
object.Equals(j, i) //false (2)
i.Equals(j) //false (3)
j.Equals(i) //true (4)
i == j //true (5)
j == i //true (6)
i.GetHashCode() == j.GetHashCode() //false (7)
Run Code Online (Sandbox Code Playgroud)
如果两个对象不表示相同的对象引用且都不为null,则它调用objA.Equals(objB)并返回结果.这意味着如果objA重写Object.Equals(Object)方法,则调用此覆盖.
我很感兴趣,如果有人能解释为什么我认为在相当基本的.NET类型中观察到不一致的行为.
我正在读一篇名为Java理论和实践的文章中引用的段落:哈希 - 有效且正确地定义hashCode()和equals()
定义相等性 Object类有两种方法可以推断出对象的身份:equals()和hashCode().通常,如果覆盖其中一个方法,则必须覆盖它们,因为它们之间必须保持重要的关系.特别是,如果两个对象根据equals()方法相等,它们必须具有相同的hashCode()值(尽管反之通常不正确).[强调我加入]
我的问题涉及该段的后一部分"虽然反过来通常不正确".如何让一个类的两个不同实例具有相同的hashCode但不相等?
一些类由框架填充(如bean).因此,您无法保证所有字段都已设置.
示例:标记为@Entity通常具有Integer id字段的类.hashCode可以写成:
public int hashCode() {
return id.hashCode();
}
Run Code Online (Sandbox Code Playgroud)
但是defencive代码可能看起来像:
public int hashCode() {
return (id != null) ? id.hashCode() : 0;
}
Run Code Online (Sandbox Code Playgroud)
我是否需要使用try { ... } catch (Exception e)in hashCode和equals函数对null或环绕码进行写检查?
我没有关于defencive编码的论据就是这种情况,因为它隐藏了将不一致的对象隐藏到集合中并导致延迟错误.我错了吗?
更新我写了这样的代码:
import java.util.*;
class ExceptionInHashcode {
String name;
ExceptionInHashcode() { }
ExceptionInHashcode(String name) { this.name = name; }
public int hashCode() {
// throw new IllegalStateException("xxx");
return this.name.hashCode();
}
public static void main(String args[]) { …Run Code Online (Sandbox Code Playgroud) Java Object契约的一个基本部分是该hashCode()方法应该与该equals()方法一致.这是有道理的并且易于理解:如果两个对象在某种程度上"相等",则它们应该返回相同的哈希码.如果没有,你可以将一个对象放在一个HashSet,例如,然后检查一个单独的实例是否在集合中并错误地返回false,即使该equals()方法会认为对象是等价的.
事实上,从Java 6开始,Java的URI代码就存在这个问题.试试这段代码:
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.net.URI;
import org.junit.Test;
public class URITest
{
@Test
public void testURIHashCode()
{
final URI uri1 = URI.create("http://www.example.com/foo%2Abar");
final URI uri2 = URI.create("http://www.example.com/foo%2abar");
assertThat("URIs are not equal.", uri1, equalTo(uri2));
assertThat("Equal URIs do not have same hash code.", uri1.hashCode(), equalTo(uri2.hashCode()));
}
}
Run Code Online (Sandbox Code Playgroud)
根据RFC 3968,URI转义序列不区分大小写; 也就是说,%2A和%2a被认为是等同的.Java URI.equals()实现将此考虑在内.但是,URI.hashCode()实施并没有考虑到这一点!这意味着,返回两个URI的实例true为URI.equals()但可以返回不同的散列码,如在示出的代码以上!
我提交了这个问题,据说导致了Java …
在String.hashiOS的文档中,它说:
在OS X版本中,不应该依赖具有相同散列值的此属性.
(奇怪的是他们在iOS文档中谈到OS X)
好吧,我需要一个不会因iOS版本而改变的hasshing功能.它可以很简单,我不需要像SHA这样的东西.那有些图书馆吗?
有关于这一个问题在这里,但接受的(唯一的)答案有只是说,我们应该尊重在文档的说明.
hashcode ×10
java ×5
equals ×4
hash ×4
.net ×3
c# ×3
bit-shift ×1
collections ×1
dictionary ×1
exception ×1
gethashcode ×1
hashmap ×1
immutability ×1
ios ×1
nsstring ×1
primes ×1
swift ×1
uri ×1