出自 《java puzzle》
本谜题试图从前一个谜题中吸取教训。下面的程序还是由一个Name类和一个main方法构成,这个main方法还是将一个名字放置到一个散列集合中,然后检查该集合是否包含了这个名字。然而,这一次Name类已经覆写了hashCode方法。那么下面的程序将打印出什么呢?
import java.util.*;
public class Name {
private String first, last;
public Name(String first, String last) {
this.first = first; this.last = last;
}
public boolean equals(Name n) {
return n.first.equals(first) && n.last.equals(last);
}
public int hashCode() {
return 31 * first.hashCode() + last.hashCode();
}
public static void main(String[ ] args) {
Set s = new HashSet();
s.add(new Name("Donald", "Duck"));
System.out.println(
s.contains(new Name("Donald", "Duck")));
}
}
该程序的main方法创建了两个Name实例,它们表示的是相同的名字。这一次使用的名字是Donald Duck而不是Mickey Mouse,但是它们不应该有很大的区别。main方法同样还是将第一个实例置于一个散列集合中,然后检查该集合中是否包含了第二个实例。这一次hashCode方法明显是正确的,因此看起来该程序应该打印true。但是,表象再次欺骗了我们:它总是打印出false。这一次又是哪里出错了呢?
在本谜题中,Name覆写了hashCode方法,但是没有覆写equals方法。这并不是说Name没有声明一个equals方法,它确实声明了,但是那是个错误的声明。Name类声明了一个参数类型是Name而不是Object的equals方法。这个类的作者可能想要覆写equals方法,但是却错误地重载了它[JLS 8.4.8.1, 8.4.9]。
HashSet类是使用equals(Object)方法来测试元素的相等性的;Name类中声明一个equals(Name)方法对HashSet不造成任何影响。那么Name是从哪里得到了它的equals(Object)方法的呢?它是从Object哪里继承而来的。这个方法只有在它的参数与在其上调用该方法的对象完全相同时才返回true。我们的程序中的main方法将一个Name实例插入到了散列集合中,并且测试另一个实例是否存在于该散列集合中,由此可知该测试一定是返回false的。对我们而言,两个实例可以代表那令人惊奇的水禽唐老鸭,但是对散列映射表而言,它们只是两个不相等的对象。
订正该程序只需用覆写的equals方法来替换重载的equals方法即可。通过使用这个equals方法,该程序就可以打印出我们所期望的true:
public boolean equals(Object o) {
if (!(o instanceof Name))
return false;
Name n = (Name)o;
return n.first.equals(first) && n.last.equals(last);
}
要让该程序可以正常工作,你只需增加一个覆写的equals方法即可。你不必剔除那个重载的版本,但是你最好是删掉它。重载为错误和混乱提供了机会[EJ Item 26]。如果兼容性要求强制你必须保留一个自身类型的equals方法,那么你应该用自身类型的重载去实现Object的重载,以此来确保它们具有相同的行为:
public boolean equals(Object o) { return o instanceof Name && equals((Name) o); }
本谜题的教训是:当你想要进行覆写时,千万不要进行重载。为了避免无意识地重载,你应该机械地对你想要覆写的每一个超类方法都拷贝其声明,或者更好的方式是让你的IDE帮你去做这些事。这样做除了可以保护你免受无意识的重载之害,而且还可以保护你免受拼错方法名之害。如果你使用的5.0或者更新的版本,那么对于那些意在覆写超类方法的方法,你可以将@Override注释应用于每一个这样的方法的声明上:
@Override public Boolean equals(Object o) { ... }
在使用这个注释时,除非被注释的方法确实覆写了一个超类方法,否则它将不能编译。对语言设计者来说,值得去考虑在每一个覆写超类方法的方法声明上都添加一个强制性的修饰符。
分享到:
相关推荐
然而,使用相同的盐值对所有用户进行散列仍然是不安全的,因为相同的密码会产生相同的散列值。理想的做法是为每个用户生成一个唯一的盐值,并将其与散列值一起存储。 在PHP中,可以使用`random_bytes`函数生成随机...
这意味着,在理论上,攻击者可以通过构造特定的数据找到两个不同的输入,这两个输入会产生相同的散列值。这对依赖SHA-1算法保护的数据完整性带来了威胁。 #### 六、未来趋势 - **SHA-3**:为了应对SHA-1的安全性...
彩虹表攻击是通过预先计算大量哈希值来查找可能的明文,而碰撞攻击则是寻找两个不同的输入产生相同哈希值的情况,这都可能弱化单向散列的安全性。 为了提高密码系统的安全性,通常会结合使用盐值(salt)和加盐哈希...
SHA-1的工作原理可以概括为:它接受任意长度的输入(消息),通过一系列复杂的数学运算,将其转化为固定长度的输出,这个输出就是散列值。由于输入数据的微小变化都会导致散列值的显著不同,因此散列函数具有单向性...
Tiger-192是一种高性能且安全的散列算法,由Ron Rivest设计,产生192位的散列值。它以其快速的计算速度和良好的安全性而闻名。`tiger_192.cpp`和`tiger_192.h`文件包含了Tiger-192算法的C++实现。 `byte_and_ints....
5. **二次预像抵抗性**:给定一个输入及其散列值,难以找到另一个不同输入产生同样的散列值。 #### 三、穷举密码算法 根据题目描述,这里提到的“穷举密码算法”主要指的是一种尝试所有可能的组合来破解密码的方法...
散列的基本思想是通过一个特定的算法(散列函数),将任意长度的输入(也称为键或关键字)转化为固定长度的输出,这个输出被称为散列值或哈希码。散列函数的设计目标是使得不同的输入产生不同的散列值,以减少冲突。...
MD5散列函数是一种广泛应用的密码学散列函数,它能将任意长度的数据转化为固定长度的128位(16字节)摘要。在MATLAB中实现MD5散列功能,可以用于数据完整性校验、密码存储以及文件校验等场景。下面将详细介绍MD5散列...
一个良好的散列函数应当能够快速计算出结果,并且具有较高的散列值分布均匀性,从而减少不同输入产生相同散列值的概率,即减少“冲突”。 ### 散列函数的关键目标 #### 减少冲突 - **定义**:“冲突”指的是不同的...
不过,`Math.random()`并不直接提供散列功能,因为它产生的随机数可能不足以满足特定的需求。例如,它不保证相同的字符串每次都会得到相同的散列值,这在许多应用中是必要的。 为了实现一个稳定的字符串散列函数,...
在IT行业中,散列(Hashing)是一种广泛应用于数据结构和算法中的技术,它通过一个特定的函数(称为散列函数)将任意长度的输入(通常是一个字符串或对象)转换成固定长度的输出,这个输出被称为散列值或哈希码。...
SHA-1算法的工作原理基于密码学中的散列函数,它接受任意长度的输入(也称为预映射或消息),然后通过一系列的数学运算,如位操作、加法、异或等,生成一个固定长度的输出,即散列值。对于SHA-1来说,这个输出是一个...
HMAC(Hash-based Message Authentication Code)是一种用于验证数据完整性和来源的加密机制,它结合了散列函数(如SHA-2系列)与密钥来产生一个消息认证码。HMAC在信息安全领域扮演着重要的角色,因为它是确保网络...
有损投影散列(Lossy Projective Hashing)是一种新的密码学原语,它在投影散列的理论与应用领域中起到了桥梁的作用,连接了对偶投影散列(Dual Projective Hashing)与光滑投影散列(Smooth Projective Hashing)。...
然而,散列函数的一个缺点是相同的输入会产生相同的输出,这就可能导致"碰撞",即两个不同的密码可能产生相同的散列值。为了增强安全性,通常会在密码加密时加入"盐"。 (2) 盐值 盐值是附加到密码之前或之后的一段...
接着,这个数据串被输入到MD5算法中进行哈希运算,产生一个固定长度的散列值,也就是授权码。这个授权码是根据特定用户和特定应用环境生成的,因此每个用户和每个应用实例都会得到不同的授权码。 在实际应用中,...
它的全称是Message-Digest Algorithm 5,主要用于产生固定长度(128位,通常以32位十六进制数表示)的摘要,用于验证数据的完整性和真实性。MD5算法虽然在安全性方面已经不再被视为安全,但由于其计算效率高、结果...
单向散列函数常用于消息认证码(MAC)、数字签名和数据完整性检查。其工作原理通常涉及一个压缩函数,将输入消息分块处理,最终生成固定长度的摘要。 在实际应用中,单向散列函数的选取至关重要,因为它们的安全性...
散列数据散列是近似最近邻搜索的一种方法,它通过将高维数据编码为紧凑的二进制码,使得两点之间的距离可以通过它们的哈希码之间的汉明距离来近似。这种方法显著提高了大规模相似性搜索的效率,具体体现在存储空间的...
它也产生128位的散列值,由于其设计上的改进,MD5比MD4更难以破解。然而,随着时间的推移,MD5的安全性也被逐步质疑。特别是自2004年以来,研究人员发现可以构造具有相同MD5散列值的不同消息,这使得MD5在需要强安全...