HashSet操作注意

来源:互联网 发布:移动网络怎样优化 编辑:程序博客网 时间:2024/04/30 21:26

使用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;}@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + age;result = prime * result + ((name == null) ? 0 : name.hashCode());return result;}@Overridepublic 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());}}



输出的结果为:2

如果在输出之前对set做remove操作呢:


set.add(user2);set.remove(user1);System.err.println(set.size());



相信大家都知道输出的结果为:1

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,这样得到的结果,相信大家都应该知道的。具体结果是什么?大笑