副标题#e#
这一章,我们对HashSet举办进修。
我们先对HashSet有个整体认识,然后再进修它的源码,最后再通过实例来学会利用HashSet。
第1部门 HashSet先容
HashSet 简介
HashSet 是一个没有反复元素的荟萃。
它是由HashMap实现的,不担保元素的顺序,并且HashSet答允利用 null 元素。
HashSet长短同步的。假如多个线程同时会见一个哈希 set,而个中至少一个线程修改了该 set,那么它必需 保持外部同步。这凡是是通过对自然封装该 set 的工具执行同步操纵来完成的。假如不存在这样的工具,则应该利用 Collections.synchronizedSet 要领来“包装” set。最亏得建设时完成这一操纵,以防备对该 set 举办意外的差异步会见:
Set s = Collections.synchronizedSet(new HashSet(…));
HashSet通过iterator()返回的迭代器是fail-fast的。
HashSet的担任干系如下:
java.lang.Object java.util.AbstractCollection<E> java.util.AbstractSet<E> java.util.HashSet<E> public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable { }
HashSet与Map干系如下图:
查察本栏目
HashSet的结构函数
// 默认结构函数 public HashSet() // 带荟萃的结构函数 public HashSet(Collection<? extends E> c) // 指定HashSet初始容量和加载因子的结构函数 public HashSet(int initialCapacity, float loadFactor) // 指定HashSet初始容量的结构函数 public HashSet(int initialCapacity) // 指定HashSet初始容量和加载因子的结构函数,dummy没有任何浸染 HashSet(int initialCapacity, float loadFactor, boolean dummy)
HashSet的主要API
boolean add(E object) void clear() Object clone() boolean contains(Object object) boolean isEmpty() Iterator<E> iterator() boolean remove(Object object) int size()
第2部门 HashSet源码理会
为了更相识HashSet的道理,下面临HashSet源码代码作出阐明。
package java.util; public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable { static final long serialVersionUID = -5024744406713321676L; // HashSet是通过map(HashMap工具)生存内容的 private transient HashMap<E,Object> map; // PRESENT是向map中插入key-value对应的value // 因为HashSet中只需要用到key,而HashMap是key-value键值对; // 所以,向map中添加键值对时,键值对的值牢靠是PRESENT private static final Object PRESENT = new Object(); // 默认结构函数 public HashSet() { // 挪用HashMap的默认结构函数,建设map map = new HashMap<E,Object>(); } // 带荟萃的结构函数 public HashSet(Collection<? extends E> c) { // 建设map。 // 为什么要挪用Math.max((int) (c.size()/.75f) + 1, 16),从 (c.size()/.75f) + 1 和 16 中选择一个较量大的树呢? // 首先,说明(c.size()/.75f) + 1 // 因为从HashMap的效率(时间本钱和空间本钱)思量,HashMap的加载因子是0.75。 // 当HashMap的“阈值”(阈值=HashMap总的巨细*加载因子) < “HashMap实际巨细”时, // 就需要将HashMap的容量翻倍。 // 所以,(c.size()/.75f) + 1 计较出来的正好是总的空间巨细。 // 接下来,说明为什么是 16 。 // HashMap的总的巨细,必需是2的指数倍。若建设HashMap时,指定的巨细不是2的指数倍; // HashMap的结构函数中也会从头计较,找出比“指定巨细”大的最小的2的指数倍的数。 // 所以,这里指定为16是从机能思量。制止反复计较。 map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16)); // 将荟萃(c)中的全部元素添加到HashSet中 addAll(c); } // 指定HashSet初始容量和加载因子的结构函数 public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<E,Object>(initialCapacity, loadFactor); } // 指定HashSet初始容量的结构函数 public HashSet(int initialCapacity) { map = new HashMap<E,Object>(initialCapacity); } HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor); } // 返回HashSet的迭代器 public Iterator<E> iterator() { // 实际上返回的是HashMap的“key荟萃的迭代器” return map.keySet().iterator(); } public int size() { return map.size(); } public boolean isEmpty() { return map.isEmpty(); } public boolean contains(Object o) { return map.containsKey(o); } // 将元素(e)添加到HashSet中 public boolean add(E e) { return map.put(e, PRESENT)==null; } // 删除HashSet中的元素(o) public boolean remove(Object o) { return map.remove(o)==PRESENT; } public void clear() { map.clear(); } // 克隆一个HashSet,并返回Object工具 public Object clone() { try { HashSet<E> newSet = (HashSet<E>) super.clone(); newSet.map = (HashMap<E, Object>) map.clone(); return newSet; } catch (CloneNotSupportedException e) { throw new InternalError(); } } // java.io.Serializable的写入函数 // 将HashSet的“总的容量,加载因子,实际容量,所有的元素”都写入到输出流中 private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { // Write out any hidden serialization magic s.defaultWriteObject(); // Write out HashMap capacity and load factor s.writeInt(map.capacity()); s.writeFloat(map.loadFactor()); // Write out size s.writeInt(map.size()); // Write out all elements in the proper order. for (Iterator i=map.keySet().iterator(); i.hasNext(); ) s.writeObject(i.next()); } // java.io.Serializable的读取函数 // 将HashSet的“总的容量,加载因子,实际容量,所有的元素”依次读出 private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in any hidden serialization magic s.defaultReadObject(); // Read in HashMap capacity and load factor and create backing HashMap int capacity = s.readInt(); float loadFactor = s.readFloat(); map = (((HashSet)this) instanceof LinkedHashSet ? new LinkedHashMap<E,Object>(capacity, loadFactor) : new HashMap<E,Object>(capacity, loadFactor)); // Read in size int size = s.readInt(); // Read in all elements in the proper order. for (int i=0; i<size; i++) { E e = (E) s.readObject(); map.put(e, PRESENT); } } }
说明:
#p#副标题#e#
#p#分页标题#e#
HashSet的代码实际上很是简朴,通过上面的注释应该很可以或许看懂。它是通过HashMap实现的,若对HashSet的领略有坚苦,发起先进修以下HashMap;学完HashMap之后,在进修HashSet就很是容易了。
第3部门 HashSet遍历方法
3.1 通过Iterator遍历HashSet
第一步:按照iterator()获取HashSet的迭代器。
第二步:遍历迭代器获取各个元素。
// 假设set是HashSet工具
for(Iterator iterator = set.iterator();
iterator.hasNext(); ) {
iterator.next();
}
3.2 通过for-each遍历HashSet
第一步:按照toArray()获取HashSet的元素荟萃对应的数组。
第二步:遍历数组,获取各个元素。
// 假设set是HashSet工具,而且set中元素是String范例
String[] arr = (String[])set.toArray(new String[0]);
for (String str:arr)
System.out.printf("for each : %s\n", str);
HashSet的遍历测试措施如下:
import java.util.Random; import java.util.Iterator; import java.util.HashSet; /* * @desc 先容HashSet遍历要领 * * @author skywang */ public class HashSetIteratorTest { public static void main(String[] args) { // 新建HashSet HashSet set = new HashSet(); // 添加元素 到HashSet中 for (int i=0; i<5; i++) set.add(""+i); // 通过Iterator遍历HashSet iteratorHashSet(set) ; // 通过for-each遍历HashSet foreachHashSet(set); } /* * 通过Iterator遍历HashSet。推荐方法 */ private static void iteratorHashSet(HashSet set) { for(Iterator iterator = set.iterator(); iterator.hasNext(); ) { System.out.printf("iterator : %s\n", iterator.next()); } } /* * 通过for-each遍历HashSet。不推荐!此要领需要先将Set转换为数组 */ private static void foreachHashSet(HashSet set) { String[] arr = (String[])set.toArray(new String[0]); for (String str:arr) System.out.printf("for each : %s\n", str); } }
第4部门 HashSet示例
#p#分页标题#e#
下面我们通过实例进修如何利用HashSet
import java.util.Iterator; import java.util.HashSet; /* * @desc HashSet常用API的利用。 * * @author skywang */ public class HashSetTest { public static void main(String[] args) { // HashSet常用API testHashSetAPIs() ; } /* * HashSet除了iterator()和add()之外的其它常用API */ private static void testHashSetAPIs() { // 新建HashSet HashSet set = new HashSet(); // 将元素添加到Set中 set.add("a"); set.add("b"); set.add("c"); set.add("d"); set.add("e"); // 打印HashSet的实际巨细 System.out.printf("size : %d\n", set.size()); // 判定HashSet是否包括某个值 System.out.printf("HashSet contains a :%s\n", set.contains("a")); System.out.printf("HashSet contains g :%s\n", set.contains("g")); // 删除HashSet中的“e” set.remove("e"); // 将Set转换为数组 String[] arr = (String[])set.toArray(new String[0]); for (String str:arr) System.out.printf("for each : %s\n", str); // 新建一个包括b、c、f的HashSet HashSet otherset = new HashSet(); otherset.add("b"); otherset.add("c"); otherset.add("f"); // 克隆一个removeset,内容和set一模一样 HashSet removeset = (HashSet)set.clone(); // 删除“removeset中,属于otherSet的元素” removeset.removeAll(otherset); // 打印removeset System.out.printf("removeset : %s\n", removeset); // 克隆一个retainset,内容和set一模一样 HashSet retainset = (HashSet)set.clone(); // 保存“retainset中,属于otherSet的元素” retainset.retainAll(otherset); // 打印retainset System.out.printf("retainset : %s\n", retainset); // 遍历HashSet for(Iterator iterator = set.iterator(); iterator.hasNext(); ) System.out.printf("iterator : %s\n", iterator.next()); // 清空HashSet set.clear(); // 输出HashSet是否为空 System.out.printf("%s\n", set.isEmpty()?"set is empty":"set is not empty"); } }