都知道HashSet中不能存放重复元素,有时候能够用来作去重操做等。可是其内部是怎么保证元素不重复的呢?下面从源码去看看。java
打开HashSet源码,发现其内部维护了一个HashMap:this
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<>(); } ... }
HashSet的构造方法其实就是在内部实例化了一个HashMap对象。其中还会看到一个static final的PRESENT变量,这个稍候再说,其实没什么实际用处。code
想知道为何HashSet不能存放重复对象,那么第一步固然是看它的add方法怎么进行的判重,代码以下:对象
public boolean add(E e) { return map.put(e, PRESENT)==null; }
。。。好吧,就把元素存放在了map里面。可是值得注意的是元素值做为的是map的key,map的value则是前面提到的PRESENT变量,这个变量只做为放入map时的一个占位符而存在,因此没什么实际用处。ci
其实,这时候答案已经出来了:HashMap的key是不能重复的,而这里HashSet的元素又是做为了map的key,固然也不能重复了。源码
HashSet怎么作到保证元素不重复的缘由找到了,文章也就结束了。。。等等,顺便看一下HashMap里面又是怎么保证key不重复的吧,代码以下:hash
public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int hash = hash(key); 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; }
其中最关键的一句:it
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
调用了对象的hashCode和equals方法进行的判断,因此又得出一个结论:若要将对象存放到HashSet中并保证对象不重复,应根据实际状况将对象的hashCode方法和equals方法进行重写io