我所要讲解的问题是,为什么存放在HashSet里面的对象,如果重写了equals()方法一定要写重写hashcode()方法,也就是说为什么要保证equals()方法比较相等的对象,其hashcode()方法返回值也要一样才可以。
首先,我给出一个例子大家看看,写一个Person类,只是覆盖了equals()方法。
class Person{
private String name;
private int age;
public Person(int age, String name) {
super();
this.age = age;
this.name = name;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) 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;
}
@Override
public String toString() {
return this.name + " :" + this.age;
}
}
下面给出测试类的代码:
public class HashSetResearch {
public static void main(String[] args) {
Set<Person> s = new HashSet<Person>();
Person p1 = new Person(22,"zhongyao");
Person p2 = new Person(22,"zhongyao");
s.add(p1);
s.add(p2);
for(Person temp : s){
System.out.println(temp);
}
}
}
程序运行结果为:
zhongyao :22
zhongyao :22
在HashSet中,不可以装重复的对象,这个是大家都知道的,具体描述可以见jdk中的HashSet类的相关javadoc描述
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element <tt>e</tt> to this set if
* this set contains no element <tt>e2</tt> such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns <tt>false</tt>.
*
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
e==null ? e2==null : e.equals(e2), 这说明在HashSet里不能拥有和e相同的element的,相同的条件是同时为null或者equals()方法返回true。这时候你可能会问,那为什么上面的p2会被加入到s中呢?
在你调用HashSet的时候发生了很多事情,其中就有用到对象的hashcode()方法,我将把整个流程的调用过程详细列出。
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
这个HashSet源代码中的add方法。
map是HashSet实例中的一个成员变量:
private transient HashMap<E,Object> map;
PRESENT也是一个成员变量,不过比较特别:
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
这个只是一个”傀儡值”,后面你会看到,在放入到map中是会作为value。
既然它调用了HashMap的put(K k, V v)方法,我们就去看看这个方法。这个代码不是很长,认真看还是很好懂的。为了方便,我还是把我的个人理解的注释放到代码之中了
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);//这个就是键值为空,不用多说了
//获取你放入的key对象的hashcode,看上面就知道这个key指向的就是你想要
//插入的对象,当然在HashMap里做了进一步的处理,事实上就是一些逻辑运算
//有兴趣的可以自己查看
int hash = hash(key.hashCode());
//这个i很有用处,从下面的代码可以看出它是定位你要插入的对象放入到table中的
//位置的,这个table是一个Entry类的数组,是个成员变量,后面会给大家看这个类的
//源代码。从indexFor()的源代码来看只有一行,简单的说就是i=hash&(talbe.length-1)
//有点hash函数的味道。
int i = indexFor(hash, table.length);
//这个for循环就是为你要插入的对象找到一个更精确的位置,可能在table中i的地方已经有
//人了,那么就要找下一个,如果大家有比较好的数据结构的功底的话应该比较容易理解,
//这里处理hash冲突的方法就是在表中具有相同hashcode那一项做链表处理(链地址法)
//这些仅仅从for语句的括弧中的三句就可以看的出来
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//以下的这个条件满足,你要插入的对象才不会被插入
//首先第一个就是hashcode,这个不难理解,记得运算是可逆的
//((k = e.key) == key这个的意思是你要插入的对象和当前遍历到的e指向同一个对
//像,当然不能被加入。后面这个key.equals(k)就不多说了。
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);// do nothing
//以上是存在相同时做出新值代替旧值
return oldValue;
}
}
modCount++;
//通过上面的for循环找到一个位置了,那么就可以在该方法里直接加如就可以了,这个
//就交给你们去查看了
addEntry(hash, key, value, i);
return null;
}
附HashSet.Entry的源代码:
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
final int hash;
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
public final K getKey() {
return key;
}
public final V getValue() {
return value;
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
Object k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
public final int hashCode() {
return (key==null ? 0 : key.hashCode()) ^
(value==null ? 0 : value.hashCode());
}
public final String toString() {
return getKey() + "=" + getValue();
}
/**
* This method is invoked whenever the value in an entry is
* overwritten by an invocation of put(k,v) for a key k that's already
* in the HashMap.
*/
void recordAccess(HashMap<K,V> m) {
}
/**
* This method is invoked whenever the entry is
* removed from the table.
*/
void recordRemoval(HashMap<K,V> m) {
}
}
这个在所有的set中几乎是差不多的,大家可以自己看看。提一点,在TreeSet中,还额外要求compareTo()返回一样,即equals()返回true时,compareTo()要返回0
问题:
在加入到hashset之后,修改对象的状态和其它的一样,那么也是可以的,不会自动断裂,这个就是我想要了解了,这个破坏了set的唯一性。
分享到:
相关推荐
在Java编程中,`equals()` 和 `hashCode()` 方法是Object类中的两个重要方法,它们在处理对象相等性以及在哈希表(如HashSet、HashMap)中起到关键作用。当自定义类时,有时需要根据业务逻辑重写这两个方法以满足...
总的来说,理解并正确地重写 `equals()` 和 `hashCode()` 方法是Java编程中的基础技能,它有助于确保对象的比较和集合操作的正确性。在开发过程中,要时刻注意这两个方法的正确实现,以提高代码质量和可维护性。
在Java编程语言中,`equals()` 和 `hashCode()` 方法是对象的基本组成部分,它们在很多场景下都发挥着至关重要的作用。这两个方法与对象的相等性比较和哈希表(如HashMap、HashSet)的运作紧密相关。这篇博客将深入...
在自定义类中,为了使对象能被HashSet或HashMap正确处理,通常需要重写hashCode()和equals()方法,确保它们遵循一致性原则。 在ArrayList和HashSet的比较中,我们可以总结以下几点: 1. 数据结构:ArrayList基于...
总结来说,`equals()` 和 `hashCode()` 在实际开发中的重写是提高代码质量、确保对象比较和哈希表操作正确性的关键。开发者需要根据业务需求来定制这两个方法,确保它们遵循上述原则,并与业务逻辑保持一致。
为了使`Person`对象能够正确地工作在`HashSet`中,我们需要重写`hashCode`方法: ```java public class Person { private String name; private int age; // 构造器、getters和setters省略... @Override ...
例如,在Hashtable、HashMap、HashSet、LinkedHashMap等容器中,我们需要重写hashcode()方法,使其生成对象的哈希码,以便于快速地查找和比较对象。 compareTo()方法是Comparable接口中的一个方法,它用于比较两个...
当我们在自定义类中重写 `equals()` 方法时,通常也需要重写 `hashCode()` 方法,这是为了遵循一些重要的原则和保证数据结构如 `HashMap`、`HashSet` 的正确行为。 首先,`equals()` 方法用于比较两个对象是否相等...
总结来说,Java序列化用于保存和传输对象,而重写`equals`和`hashCode`方法则是为了确保对象比较和在哈希表中的操作符合预期。在实际开发中,理解并恰当地应用这些概念对于构建高效、健壮的Java应用程序至关重要。
在Java编程语言中,`equals()` 和 `hashCode()` 方法对于对象的比较和处理至关重要,特别是在集合框架中。这两个方法有着紧密的联系,因为它们在决定对象是否相等以及如何存储和检索对象时起到关键作用。 首先,让...
在Java编程中,`equals()`和`hashCode()`方法是Object类中的两个重要方法。...如果你希望了解更多有关`equals()`和`hashCode()`的知识,以及IDEA中的其他快捷键和功能,可以查阅IDEA的帮助文档或者相关的教程资源。
在Java编程语言中,`hashCode()` 和 `equals()` 方法是两个非常重要的概念,尤其是在处理对象比较和哈希表(如 `HashMap` 或 `HashSet`)时。这两个方法来源于 `Object` 类,是所有Java类的基类,因此,每个自定义类...
例如,在 HashSet 中,我们可以使用 hashcode 方法来比较两个对象是否相同。如果两个对象的散列码相同,我们可以认为它们是相同的对象。 == 号、equals 方法和 hashcode 方法都是用来比较对象的,但是它们之间存在...
总之,重写 `equals` 和 `hashCode` 方法是保证自定义对象在集合中正确比较和存储的关键。这样做不仅可以满足特定的业务需求,还能确保集合操作的正确性和效率。在实现类时,应始终注意这两个方法的配合使用,以遵循...
在Java编程语言中,`hashCode()`和`equals()`方法是对象身份验证的关键组成部分,它们主要用于对象的比较和哈希表(如HashMap、HashSet等)的操作。理解这两个方法的工作原理对于编写高效和可靠的代码至关重要。 ...
在实际应用中,我们需要同时重新定义 equals() 和 hashCode() 方法,以便确保对象的唯一性。 例如,在 HashSet 中,我们需要定义 equals() 方法,以便判断两个对象是否相等。如果我们不定义 equals() 方法,那么同...
在C#编程中,`Equals` 和 `GetHashCode` 方法是非常重要的成员方法,它们对于确保对象的正确比较以及高效地存储和检索对象至关重要。这两个方法通常需要在自定义类中进行重写,以满足特定业务逻辑的需求。 #### 1. ...
在Java编程语言中,`equals()` 和 `hashCode()` 是两个非常重要的方法,它们主要用于比较对象的平等性以及在散列数据结构(如 `HashMap`、`HashSet` 等)中的对象定位。当我们创建自定义类时,有时需要根据业务需求...
在Java编程语言中,`equals()`和`hashCode()`方法是对象的基本组成部分,它们主要用于对象的比较和存储。这两个方法在`java.lang.Object`类中定义,因此所有的Java类都默认继承了这两个方法。然而,根据具体的应用...
在这个类中,很可能已经重写了`equals()`, `hashCode()` 和 `toString()` 方法,以便更好地处理对象的比较、哈希存储和输出信息。 为了实现`DBObject`类的正确行为,开发者可能考虑了以下几点: 1. 在`equals()`...