没有多少Java程序员知道HashSet 在Java内部是用HashMap 实现的,所以如果你知道HashMap在Java内部是如何工作的,更有可能你能搞清楚HashSet在Java中是如何工作的。但是,现在一个好奇的Java开发者可以质疑,为什么HashSet 会使用HashMap,因为你需要一个键值对来使用Map,而在HashSet 中我们只存储一个对象。好问题,不是吗?如果你还记得早期类的一些功能,那么你就会知道HashMap允许重复的值,在Java中实现HashSet时就利用了这个属性。
由于HashSet实现了Set接口,它需要保证唯一性,而这是通过将元素作为键来存储的,其值总是相同的。通过查看JDK源代码中的HashSet.java,事情就变得很清楚了。
你所需要看的是,元素是如何存储在HashSet中的,以及如何从HashSet中检索出来的。由于HashSet没有提供任何直接检索对象的方法,例如从HashMap 中获取(Key key)或从List中获取(int index),从HashSet 中获取对象的唯一方法是通过迭代器。关于Java中对HashSet进行迭代的代码示例,请看这里 。
当你在Java中创建一个HashSet的对象时,它在内部会创建一个备份Map的实例,默认初始容量为16,默认负载系数为0.75,如下所示:
/**
* 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<>();
}
现在让我们看看Java中java.util.HashSet的add()和iterate()方法的代码,以了解HashSet在Java中的内部工作。
在Java中,对象是如何存储在HashSet中的?
如下所示,对add(Object)的调用是对put(Key, Value)的内部委托,其中Key 是你传递的对象,value是另一个对象,称为PRESENT,它是java.util.HashSet中的一个常数,如下所示。
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
由于PRESENT 是一个常数,对于所有的键,我们在备份HashMap中都有相同的值,称为map。
如何从HashSet中检索对象?
现在让我们看看在Java中获取遍历HashSet的迭代器的代码。java.util.HashSet类中的iterator()方法返回map.keySet().iterator() 方法所返回的备份Map的迭代器 。
/**
* Returns an iterator over the elements in this set. The elements
* are returned in no particular order.
*
* @return an Iterator over the elements in this set
* @see ConcurrentModificationException
*/
public Iterator<E> iterator() {
return map.keySet().iterator();
}
如何在Java中使用HashSet - 示例
在Java中使用HashSet 是非常简单的,不要认为它是Map ,而是更像Collection,即 使用add()方法添加元素,检查其返回值,看该对象是否已经存在于HashSet 中。同样,在Java中使用一个迭代器来检索HashSet 中的元素。
你也可以使用contains()方法来检查一个对象是否已经存在于HashSet 中。这个方法使用equals()方法来比较对象的匹配。你也可以使用remove()方法来从HashSet中移除对象。由于HashSet 的元素被用作备份HashMap的键,它们必须实现equals()和hashCode()方法。
不可变性不是一个要求,但是如果它是不可变的,那么你可以认为对象在集合中停留期间不会被改变。下面的例子展示了HashSet 在Java中的基本用法,对于更高级的例子,你可以查看这个 教程。
import java.util.HashSet;
import java.util.Iterator;
/** * Java Program to demonstrate How HashSet works internally in Java. * @author http://java67.com */
public class HashSetDemo{
public static void main(String args[]) {
HashSet<String> supportedCurrencies = new HashSet<String>();
// adding object into HashSet, this will be translated to put() calls
supportedCurrencies.add("USD");
supportedCurrencies.add("EUR");
supportedCurrencies.add("JPY");
supportedCurrencies.add("GBP");
supportedCurrencies.add("INR");
supportedCurrencies.add("CAD");
// retrieving object from HashSet Iterator<String> itr =
supportedCurrencies.iterator();
while(itr.hasNext()){
System.out.println(itr.next());
}
}
}
输出
JPY
EUR
INR
USD
CAD
GBP
以上就是关于HashSet如何在Java中实现以及HashSet如何在内部工作的全部内容。正如我所说,如果你知道HashMap在Java中是如何工作的,你就可以解释HashSet的工作原理了,你知道它对所有键都使用相同的值。记住,对于你要存储在HashSet中的任何对象都要覆盖equals()和hashCode(),因为你的对象在备份Map中被用作键,它必须覆盖这些方法。如果可能的话,让你的对象成为Immutable 或有效的immutable。
![How HashSet Internally Works in Java [Explained]](https://lh3.googleusercontent.com/-IKfgEwKFcOI/YMl703h-VaI/AAAAAAAAoPk/WDol6quYFbwaQtew51b-0kBnoaxKN4F4wCLcBGAsYHQ/w436-h297/image.png)