概述 1.HashSet 是一个没有重复元素的集合。它是由HashMap实现的,不保证元素的顺序,特别是它不保证该顺序恒久不变。而且HashSet允许使用 null 元素。 2.HashSet是非同步的。如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须保持外部同步。这通常是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装” Set。最好在创建时完成这一操作,以防止对该 set 进行意外的不同步访问:
1 Set s = Collections.synchronizedSet(new HashSet (...));
3.HashSet通过iterator()返回的迭代器是fail-fast的。 4.HashSet的继承关系如下:
1 2 3 public class HashSet <E> extends AbstractSet <E> implements Set <E>, Cloneable, java.io.Serializable
HashSet实现 HashSet是基于HashMap实现的,底层使用HashMap来保存所有元素,相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成.
HashSet属性 1 2 3 4 1. private transient HashMap<E,Object> map; 2. 3. 4. private static final Object PRESENT = new Object ();
可以看到,HashSet 实际上是使用 HashMap 来保存数据的。而且主要用的是 HashMap的 key。 PRESENT是什么东西呢?看上面的注释。dummy的意思是 挂名代表,傀儡。所以,可知: PRESENT是向map中插入key-value对应的value 因为HashSet中只需要用到key,而HashMap是key-value键值对; 所以,向map中添加键值对时,键值对的值固定是PRESENT。每个set集合 中的元素都是HashMap的key 值(这也就保证了HashSet集合中不能有重复元素),而 它们的value值都是 PRESENT。
HashSet构造函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 1. 6. public HashSet () { 7. map = new HashMap <E,Object>(); 8. } 9. 15. public HashSet (Collection<? extends E> c) { 16. 17. 18. map = new HashMap <E,Object>(Math.max((int ) (c.size()/.75f ) + 1 , 16 )); 19. 20. addAll(c); 21. } 22. 29. public HashSet (int initialCapacity, float loadFactor) { 30. map = new HashMap <E,Object>(initialCapacity, loadFactor); 31. } 32. 38. public HashSet (int initialCapacity) { 39. map = new HashMap <E,Object>(initialCapacity); 40. } 41. 50. HashSet(int initialCapacity, float loadFactor, boolean dummy) { 51. map = new LinkedHashMap <E,Object>(initialCapacity, loadFactor); 52. }
HashSet方法 因为HashSet底层是HashMap实现,所以它的方法大都是直接调用HashMap的方法。
add(),remove() 1 2 3 4 5 6 7 8 9 10 11 1. 2. return map.put(e, PRESENT)==null ; 3. } 4. 5. public boolean remove (Object o) { 6. return map.remove(o)==PRESENT; 7. } 1. 2. public void clear () {3. map.clear(); 4. }
具体可参考HashMap的源码。
iterator(),size(),isEmpty() 1 2 3 4 5 6 7 8 9 10 11 12 13 1. public Iterator<E> iterator () { 2. 3. return map.keySet().iterator(); 4. } 5. public int size () { 6. return map.size(); 7. } 8. public boolean isEmpty () { 9. return map.isEmpty(); 10. } 11. public boolean contains (Object o) { 12. return map.containsKey(o); 13. }
看名字很容易就知道是干嘛的,都是直接用的HashMap的方法。
clone() 1 2 3 4 5 6 7 8 9 10 11 12 13 1. 5. public Object clone () { 6. try { 7. HashSet<E> newSet = (HashSet<E>) super .clone(); 8. newSet.map = (HashMap<E, Object>) map.clone(); 9. return newSet; 10. } catch (CloneNotSupportedException e) { 11. throw new InternalError (e); 12. } 13. }
序列化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 1. 2. 3. private void writeObject (java.io.ObjectOutputStream s) 4. throws java.io.IOException { 5. 6. s.defaultWriteObject(); 7. s.writeInt(map.capacity()); 8. s.writeFloat(map.loadFactor()); 9. 10. s.writeInt(map.size()); 11. 12. for (Iterator i=map.keySet().iterator(); i.hasNext(); ) 13. s.writeObject(i.next()); 14. } 15. 16. 17. private void readObject (java.io.ObjectInputStream s) 18. throws java.io.IOException, ClassNotFoundException { 19. 20. s.defaultReadObject(); 21. 22. int capacity = s.readInt(); 23. float loadFactor = s.readFloat(); 24. map = (((HashSet)this ) instanceof LinkedHashSet ? 25. new LinkedHashMap <E,Object>(capacity, loadFactor) : 26. new HashMap <E,Object>(capacity, loadFactor)); 27. 28. int size = s.readInt(); 29. 30. for (int i=0 ; i<size; i++) { 31. E e = (E) s.readObject(); 32. map.put(e, PRESENT); 33. } 34. }
总结 总体来说,HashSet是比较简单的,底层是HashMap实现的,使用的都是HashMap的方法。
HashSet遍历: 1 2 3 4 5 6 7 8 9 10 2. Iterator iterator = set.iterator(); 3. while (iterator.hasNext()) { 4. System.out.println(iterator.next()); 5. } 6. 7. 8. for (String s:set) { 9. System.out.println(s); 10. }
要注意的是:当我们要将一个类作为HashMap的key或者存储在HashSet的时候。通过重写hashCode()和equals(Object object)方法很重要,并且保证这两个方法的返回值一致。当两个类的hashCode()返回一致时,应该保证equasl()方法也返回true。 HashMap源码整理中…