大杂烩 -- equals、hashCode联系与区别
基础大杂烩 -- 目录
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Equals
1、默认情况(没有覆盖equals方法)下equals方法都是调用Object类的equals方法,而Object的equals方法主要用于判断对象的内存地址引用是不是同一个地址(是不是同一个对象)。
public boolean equals(Object obj) { return (this == obj); }
2 、如果类中覆盖了equals方法,那么就要根据具体的代码来确定equals方法的作用了,覆盖后一般都是通过对象的内容是否相等来判断对象是否相等。
@Override public boolean equals(Object obj) { // 1.地址相等 if (this == obj) return true; // 2.不为空 if (obj == null) return false; // 3.类型相同 if (getClass() != obj.getClass()) return false; StudentOverrideEquals other = (StudentOverrideEquals) obj; // 4.属性相等 if (age != other.age) // 4.1基本数据类型判断值 return false; // 4.2引用数据类型。1.空值判断,2.非空调用自身equals() if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; }
HashCode
1.默认情况(没有覆盖hashCode方法)下hashCode方法都是调用Object类的hashCode方法,而Object的hashCode方法的返回值的默认实现是根据对象的内存地址进行计算。
public native int hashCode();
2.如果类中覆盖了hashCode方法,那么就要根据具体的代码来确定hashCode方法的返回值,覆盖后一般都是通过对象的属性计算hashCode值。
@Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; }
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
不覆写equals、不覆写hashCode(Tt02)
覆写equals、不覆写hashCode(Tt03)
不覆写equals、 覆写hashCode(Tt04)
覆写equals、 覆写hashCode(Tt05)
覆写equals、 覆写hashCode(返回值不同)(Tt06)
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Tt02:不覆写equals、不覆写hashCode
Class : Student
package limeMianShi.duolaidian.equals_hashcode; /** * 没有覆盖equals、hashCode方法 * * @author lime * */ public class Student { private String name; private int age; public Student() { super(); } public Student(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
Class : main
package limeMianShi.duolaidian.equals_hashcode; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; /** * 没有覆盖equals、hashCode方法 * * @author lime * */ public class Test02 { public static void main(String[] args) { List<Student> list = new LinkedList<Student>(); Set<Student> set = new HashSet<Student>(); Student stu1 = new Student("lime",25); Student stu2 = new Student("lime", 25); System.out.println("stu1 == stu2 : "+(stu1 == stu2)); System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2)); System.out.println("stu1-->HashCode:" + stu1.hashCode()); System.out.println("stu2-->HashCode:" + stu2.hashCode()); list.add(stu1); list.add(stu2); System.out.println("list size:"+ list.size()); set.add(stu1); set.add(stu2); System.out.println("set size:"+ set.size()); } }
Console :
stu1 == stu2 : false stu1.equals(stu2) : false stu1-->HashCode:1865127310 stu2-->HashCode:515132998 list size:2 set size:2
Tt03:覆写equals、不覆写hashCode
Class : StudentOverrideEquals
package limeMianShi.duolaidian.equals_hashcode; /** * 覆写equals方法 * * @author lime * */ public class StudentOverrideEquals { private String name; private int age; public StudentOverrideEquals() { super(); } public StudentOverrideEquals(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public boolean equals(Object obj) { // 1.地址相等 if (this == obj) return true; // 2.不为空 if (obj == null) return false; // 3.类型相同 if (getClass() != obj.getClass()) return false; StudentOverrideEquals other = (StudentOverrideEquals) obj; // 4.属性相等 if (age != other.age) // 4.1基本数据类型判断值 return false; // 4.2引用数据类型。1.空值判断,2.非空调用自身equals() if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
Class : main
package limeMianShi.duolaidian.equals_hashcode; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; /** * 覆写equals方法 * * @author lime * */ public class Test03 { public static void main(String[] args) { List<StudentOverrideEquals> list = new LinkedList<StudentOverrideEquals>(); Set<StudentOverrideEquals> set = new HashSet<StudentOverrideEquals>(); StudentOverrideEquals stu1 = new StudentOverrideEquals("lime",25); StudentOverrideEquals stu2 = new StudentOverrideEquals("lime", 25); System.out.println("stu1 == stu2 : "+(stu1 == stu2)); System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2)); System.out.println("stu1-->HashCode:" + stu1.hashCode()); System.out.println("stu2-->HashCode:" + stu2.hashCode()); list.add(stu1); list.add(stu2); System.out.println("list size:"+ list.size()); set.add(stu1); set.add(stu2); System.out.println("set size:"+ set.size()); } }
Console :
stu1 == stu2 : false stu1.equals(stu2) : true stu1-->HashCode:1865127310 stu2-->HashCode:515132998 list size:2 set size:2
Tt04:不覆写equals、覆写hashCode
Class : StudentOverrideHashCode
package limeMianShi.duolaidian.equals_hashcode; /** * 覆盖hashCode方法 * * @author lime * */ public class StudentOverrideHashCode { private String name; private int age; public StudentOverrideHashCode(String name, int age) { super(); this.name = name; this.age = age; } public StudentOverrideHashCode() { super(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } }
Class : main
package limeMianShi.duolaidian.equals_hashcode; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; /** * 覆写hashCode方法 * * @author lime * */ public class Test04 { public static void main(String[] args) { List<StudentOverrideHashCode> list = new LinkedList<StudentOverrideHashCode>(); Set<StudentOverrideHashCode> set = new HashSet<StudentOverrideHashCode>(); StudentOverrideHashCode stu1 = new StudentOverrideHashCode("lime",25); StudentOverrideHashCode stu2 = new StudentOverrideHashCode("lime", 25); System.out.println("stu1 == stu2 : "+(stu1 == stu2)); System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2)); System.out.println("stu1-->HashCode:" + stu1.hashCode()); System.out.println("stu2-->HashCode:" + stu2.hashCode()); list.add(stu1); list.add(stu2); System.out.println("list size:"+ list.size()); set.add(stu1); set.add(stu2); System.out.println("set size:"+ set.size()); } }
Console :
stu1 == stu2 : false stu1.equals(stu2) : false stu1-->HashCode:3323549 stu2-->HashCode:3323549 list size:2 set size:2
Tt05:覆写equals、覆写hashCode
Class : StudentOverrideEqualsHashCode
package limeMianShi.duolaidian.equals_hashcode; /** * 覆盖equals 和 hashCode方法 * * @author lime * */ public class StudentOverrideEqualsHashCode { private String name; private int age; public StudentOverrideEqualsHashCode(String name, int age) { super(); this.name = name; this.age = age; } public StudentOverrideEqualsHashCode() { super(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; StudentOverrideEqualsHashCode other = (StudentOverrideEqualsHashCode) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
Class : main
package limeMianShi.duolaidian.equals_hashcode; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; /** * 覆写equals、hashCode方法 * * @author lime * */ public class Test05 { public static void main(String[] args) { List<StudentOverrideEqualsHashCode> list = new LinkedList<StudentOverrideEqualsHashCode>(); Set<StudentOverrideEqualsHashCode> set = new HashSet<StudentOverrideEqualsHashCode>(); StudentOverrideEqualsHashCode stu1 = new StudentOverrideEqualsHashCode("lime",25); StudentOverrideEqualsHashCode stu2 = new StudentOverrideEqualsHashCode("lime", 25); System.out.println("stu1 == stu2 : "+(stu1 == stu2)); System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2)); System.out.println("stu1-->HashCode:" + stu1.hashCode()); System.out.println("stu2-->HashCode:" + stu2.hashCode()); list.add(stu1); list.add(stu2); System.out.println("list size:"+ list.size()); set.add(stu1); set.add(stu2); System.out.println("set size:"+ set.size()); } }
Console :
stu1 == stu2 : false stu1.equals(stu2) : true stu1-->HashCode:3323549 stu2-->HashCode:3323549 list size:2 set size:1
Tt05:覆写equals、覆写hashCode(返回值不同)
Class : StudentOverrideEqualsHashCodeneq
package limeMianShi.duolaidian.equals_hashcode; /** * equals 相等,hashCode不等 * * @author lime * */ public class StudentOverrideEqualsHashCodeneq { private String name; private int age; public StudentOverrideEqualsHashCodeneq(String name, int age) { super(); this.name = name; this.age = age; } public StudentOverrideEqualsHashCodeneq() { super(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public int hashCode() { return (int) (Math.random()*10); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; StudentOverrideEqualsHashCodeneq other = (StudentOverrideEqualsHashCodeneq) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
Class : main
package limeMianShi.duolaidian.equals_hashcode; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; /** * 覆写equals、hashCode(返回值不同)方法 * * @author lime * */ public class Test06 extends Object{ public static void main(String[] args) { List<StudentOverrideEqualsHashCodeneq> list = new LinkedList<StudentOverrideEqualsHashCodeneq>(); Set<StudentOverrideEqualsHashCodeneq> set = new HashSet<StudentOverrideEqualsHashCodeneq>(); StudentOverrideEqualsHashCodeneq stu1 = new StudentOverrideEqualsHashCodeneq("lime",25); StudentOverrideEqualsHashCodeneq stu2 = new StudentOverrideEqualsHashCodeneq("lime", 25); System.out.println("stu1 == stu2 : "+(stu1 == stu2)); System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2)); System.out.println("stu1-->HashCode:" + stu1.hashCode()); System.out.println("stu2-->HashCode:" + stu2.hashCode()); list.add(stu1); list.add(stu2); System.out.println("list size:"+ list.size()); set.add(stu1); set.add(stu2); System.out.println("set size:"+ set.size()); } }
Console :
stu1 == stu2 : false stu1.equals(stu2) : true stu1-->HashCode:1 stu2-->HashCode:6 list size:2 set size:2
Tt07:覆写equals、覆写hashCode(修改属性值,改变hashCode,remove)
Class :StudentOverrideEqualsHashCode
package limeMianShi.duolaidian.equals_hashcode; /** * 覆盖equals 和 hashCode方法 * * @author lime * */ public class StudentOverrideEqualsHashCode { private String name; private int age; public StudentOverrideEqualsHashCode(String name, int age) { super(); this.name = name; this.age = age; } public StudentOverrideEqualsHashCode() { super(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; StudentOverrideEqualsHashCode other = (StudentOverrideEqualsHashCode) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
Class : main
package limeMianShi.duolaidian.equals_hashcode; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; /** * 覆写equals、hashCode方法。修改参与计算hashCode的属性 * * @author lime * */ public class Test07{ public static void main(String[] args) { List<StudentOverrideEqualsHashCode> list = new LinkedList<StudentOverrideEqualsHashCode>(); Set<StudentOverrideEqualsHashCode> set = new HashSet<StudentOverrideEqualsHashCode>(); StudentOverrideEqualsHashCode stu1 = new StudentOverrideEqualsHashCode("lime",25); StudentOverrideEqualsHashCode stu2 = new StudentOverrideEqualsHashCode("lime", 25); System.out.println("stu1 == stu2 : "+(stu1 == stu2)); System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2)); System.out.println("stu1-->HashCode:" + stu1.hashCode()); System.out.println("stu2-->HashCode:" + stu2.hashCode()); list.add(stu1); list.add(stu2); System.out.println("list size:"+ list.size()); set.add(stu1); set.add(stu2); System.out.println("set size:"+ set.size()); System.out.println("修改stu1的name"); stu1.setName("oracle"); System.out.println("stu1-->HashCode:" + stu1.hashCode()); set.remove(stu1); System.out.println("set size:"+ set.size()); } }
Console :
stu1 == stu2 : false stu1.equals(stu2) : true stu1-->HashCode:3323549 stu2-->HashCode:3323549 list size:2 set size:1 修改stu1的name stu1-->HashCode:-1008860090 set size:1
结果分析:
set添加元素时,首先判断hashCode一致,在判断equals一致。
set移除元素时,首先判断hashCode一致,根据hashCode移除元素。
当我们将某个对象存到set中时,如果该对象的属性参与了hashcode的计算,那么以后就不能修改该对象参与hashcode计算的那些属性了,否则会引起意向不到的错误的。正如测试中,不能够移除stu1对象。
hashSet本质是封装hashMap
package java.util; import java.io.InvalidObjectException; public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable{ static final long serialVersionUID = -5024744406713321676L; private transient HashMap<E,Object> map; private static final Object PRESENT = new Object(); public HashSet() { map = new HashMap<>(); } public HashSet(Collection<? extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); } public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); } HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); } public Iterator<E> iterator() { return map.keySet().iterator(); } public int size() { return map.size(); } public boolean isEmpty() { return map.isEmpty(); } public boolean contains(Object o) { return map.containsKey(o); } public boolean add(E e) { return map.put(e, PRESENT)==null; } public boolean remove(Object o) { return map.remove(o)==PRESENT; } public void clear() { map.clear(); } @SuppressWarnings("unchecked") public Object clone() { try { HashSet<E> newSet = (HashSet<E>) super.clone(); newSet.map = (HashMap<E, Object>) map.clone(); return newSet; } catch (CloneNotSupportedException e) { throw new InternalError(e); } } private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { s.defaultWriteObject(); s.writeInt(map.capacity()); s.writeFloat(map.loadFactor()); s.writeInt(map.size()); for (E e : map.keySet()) s.writeObject(e); } private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); int capacity = s.readInt(); if (capacity < 0) { throw new InvalidObjectException("Illegal capacity: " + capacity); } float loadFactor = s.readFloat(); if (loadFactor <= 0 || Float.isNaN(loadFactor)) { throw new InvalidObjectException("Illegal load factor: " + loadFactor); } int size = s.readInt(); if (size < 0) { throw new InvalidObjectException("Illegal size: " + size); } capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f), HashMap.MAXIMUM_CAPACITY); map = (((HashSet<?>)this) instanceof LinkedHashSet ? new LinkedHashMap<E,Object>(capacity, loadFactor) : new HashMap<E,Object>(capacity, loadFactor)); for (int i=0; i<size; i++) { @SuppressWarnings("unchecked") E e = (E) s.readObject(); map.put(e, PRESENT); } } public Spliterator<E> spliterator() { return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0); } }
set-->add:
public boolean add(E e) { return map.put(e, PRESENT)==null; }
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
set-->remove :
public boolean remove(Object o) { return map.remove(o)==PRESENT; }
public V remove(Object key) { Node<K,V> e; return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value; }
总结:
1、equals方法用于比较对象的内容是否相等(覆盖以后)
2、hashcode方法只有在集合中用到
3、当覆盖了equals方法时,比较对象是否相等将通过覆盖后的equals方法进行比较(判断对象的内容是否相等)。
4、将对象放入到集合中时,首先判断要放入对象的hashcode值与集合中的任意一个元素的hashcode值是否相等,如果不相等直接将该对象放入集合中。如果hashcode值相等,然后再通过equals方法判断要放入对象与集合中的任意一个对象是否相等,如果equals判断不相等,直接将该元素放入到集合中,否则不放入。
5、将元素放入集合的流程图:
啦啦啦
啦啦啦