使用HashSet的时候,会遇到一个很有意思的情况,大家看如下的代码:
1、定义User实体:重写hashCode和equals方法,具体什么功能,这边不做详细的介绍。
package com.enson.model;
public class User {
private int age ;
private String name ;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@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;
User other = (User) 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;
}
}
2、测试代码:
package test;
import java.util.HashSet;
import java.util.Set;
import com.enson.model.User;
public class HashSetTest {
public static void main(String[] args) {
Set<User> set = new HashSet<User>();
User user1 = new User();
user1.setAge(20);
user1.setName("enson");
set.add(user1);
User user2 = new User();
user2.setAge(10);
user2.setName("chan");
set.add(user2);
System.err.println(set.size());
}
}
如果在输出之前对set做remove操作呢:
set.add(user2);
set.remove(user1);
System.err.println(set.size());
3、看下面有意思的情况:
<pre name="code" class="java"> set.add(user2);
user1.setAge(30);
set.remove(user1);
System.err.println(set.size());
请你猜测一下,现在输出的结果是?
===========================================================================================
其实现在输出的结果仍然为:2
这是一个比较有意思的情况。当我们用HashSet进行存储对象时候,就会出现这样的情况。
想知道具体的原因,我们得从java源码的角度进行分析:
查看HashSet源码:
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;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<E,Object>();
}
我们可以看到,这边内部的实现其实是实例化一个HashMap
对于hashset的add方法:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
其实际的操作是hashmap的put和remove方法
查看HashMap:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
这边可以看到,当添加的时候,HashMap其实是根据hashcode和equals方法来判断是否是同一个对象。这样就明白了。
当set进行remove的时候,其实也是根据hashcode和equals方法来判断。当作user1.setAge(30);更改的时候,user1这个对象已经变了,与存在HashSet当中的user1已经不是同一个对象了,再remove的时候,根据变化了的user1来查找,其实已经是查找不到了。这样就导致remove失败。
再看一个意思的情况:
set.add(user2);
user1.setAge(20);
set.remove(user1);
System.err.println(set.size());
如果将年龄仍然设置为20,这样得到的结果,相信大家都应该知道的。具体结果是什么?