在业务代码中,我们通常使用 equals 或 == 进行判等操作。equals 是方法而 == 是操作符,它们的使用是有区别的:
基本类型只能使用 == ,其他类型都需要使用 equals
Integer判等案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public class IntegerEqualsTest {
@Test public void test() {
Integer a = 127; Integer b = 127; assertTrue(a == b);
Integer c = 128; Integer d = 128; assertFalse(c == d);
Integer e = 127; Integer f = new Integer(127); assertFalse(e == f);
Integer g = new Integer(127); Integer h = new Integer(127); assertFalse(g == h);
Integer i = 128; int j = 128; assertTrue(i == j); }
}
|
字符串判等案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class StringEqualsTest {
@Test public void test() { String a = "1"; String b = "1"; assertTrue(a == b);
String c = new String("2"); String d = new String("2"); assertFalse(c == d);
String e = new String("3").intern(); String f = new String("3").intern(); assertTrue( e == f);
String g = new String("4"); String h = new String("4"); assertTrue(g.equals(h)); } }
|
重写equals
实现一个更好的 equals 应该注意的点:
- 考虑到性能,可以先进行指针判等,如果对象是同一个那么直接返回 true;
- 需要对另一方进行判空,空对象和自身进行比较,结果一定是 fasle;
- 需要判断两个对象的类型,如果类型都不同,那么直接返回 false;
- 确保类型相同的情况下再进行类型强制转换,然后逐一判断所有字段。
Object equals源码
1 2 3
| public boolean equals(Object obj) { return (this == obj); }
|
String equals源码
1 2 3 4 5 6 7 8 9 10 11 12 13
| public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String aString = (String)anObject; if (coder() == aString.coder()) { return isLatin1() ? StringLatin1.equals(value, aString.value) : StringUTF16.equals(value, aString.value); } } return false; }
|
Integer equals源码
1 2 3 4 5 6
| public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; }
|
为什么要重写hashCode
如果重写了equals,比如说是基于对象的内容实现的,而没有重写hashCode,就会调用Object类中的hashCode,那么就会导致两个逻辑上相等的对象,hashCode却不一样。这样,当你用其中一个对象作为key保存到HashMap、HashSet或者Hashtable中,再以另一个“相等的”对象作为key去取值,则根本找不到。因此,为什么重写equals一定要重写hashCode,其实简单来说就是为了保证两个对象在equals相同的情况下hashCode值也必定相同。
总结一下要点:
- hashCode主要用于提升查询效率,来确定在散列结构中对象的存储地址;
- 重写equals()必须重写hashCode(),二者参与计算的自身属性字段应该相同;
- hash类型的存储结构,添加元素重复性校验的标准就是先取hashCode值,后判断equals();
- equals()相等的两个对象,hashcode()一定相等;
- 反过来:hashcode()不等,一定能推出equals()也不等;
- hashcode()相等,equals()可能相等,也可能不等。
怎么提升的效率
java中的List集合是有序可重复的,而set集合是无序不能重复的,那么怎么能保证不能被放入重复的元素呢,但靠equals方法一样比较的话,如果原来集合中以后又10000个元素了,那么放入10001个元素,难道要将前面的所有元素都进行比较,看看是否有重复,欧码噶的,这个效率可想而知,因此hashcode就应遇而生了,java就采用了hash表,利用哈希算法(也叫散列算法),就是将对象数据根据该对象的特征使用特定的算法将其定义到一个地址上,那么在后面定义进来的数据只要看对应的hashcode地址上是否有值,那么就用equals比较,如果没有则直接插入,只要就大大减少了equals的使用次数,执行效率就大大提高了。
案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| public class PersonEqualsTest {
@Test public void test() { Person lisi1 = new Person("lisi", 12); Person lisi2 = new Person("lisi", 12);
Assertions.assertFalse(lisi1.equals(lisi2)); System.out.println(lisi1.hashCode()); System.out.println(lisi2.hashCode()); Assertions.assertFalse(lisi1.hashCode() == lisi2.hashCode()); }
@Test public void testRewriteEquals() { PersonRewriteEquals lisi1 = new PersonRewriteEquals("lisi", 13); PersonRewriteEquals lisi2 = new PersonRewriteEquals("lisi", 12);
Assertions.assertTrue(lisi1.equals(lisi2)); System.out.println(lisi1.hashCode()); System.out.println(lisi2.hashCode()); Assertions.assertFalse(lisi1.hashCode() == lisi2.hashCode()); }
@Test public void testRewriteHash () { PersonRewriteHash lisi1 = new PersonRewriteHash("lisi", 13); PersonRewriteHash lisi2 = new PersonRewriteHash("lisi", 12);
Assertions.assertFalse(lisi1.equals(lisi2)); System.out.println(lisi1.hashCode()); System.out.println(lisi2.hashCode()); Assertions.assertTrue(lisi1.hashCode() == lisi2.hashCode()); }
@Test public void testRewriteEqualsAndHash () { PersonRewriteEqualsAndHash lisi1 = new PersonRewriteEqualsAndHash("lisi", 12); PersonRewriteEqualsAndHash lisi2 = new PersonRewriteEqualsAndHash("lisi", 12);
Assertions.assertTrue(lisi1.equals(lisi2)); System.out.println(lisi1.hashCode()); System.out.println(lisi2.hashCode()); Assertions.assertTrue(lisi1.hashCode() == lisi2.hashCode()); } @Test public void testRewriteEqualsAndHash2 () { PersonRewriteEqualsAndHash lisi1 = new PersonRewriteEqualsAndHash("lisi", 12); PersonRewriteEqualsAndHash lisi2 = new PersonRewriteEqualsAndHash("lisi", 12);
HashMap<PersonRewriteEqualsAndHash, String> map = new HashMap<>(); map.put(lisi1, "lisi1"); map.put(lisi2, "lisi2"); Assertions.assertEquals("lisi2",map.get(lisi1)); Assertions.assertEquals("lisi2",map.get(lisi2)); } }
|