首页   >   代码编程

HashSet的实现原理,与HashMap有何区别和联系

JDk为我们提供了一些常用的数据结构,List、Map、Set这三种是使用最广泛的,List存放连续的数据,并允许为null,Map以键值对的形式存放数据,允许key和value为null,Set则可以存储无重复的数据

1、在map中,如果使用了相同的key,后面的value就会将前面的value覆盖掉,这也相当于说是key是唯一的,那这个作用岂不是和Set一样了?

2、这二者之间又有什么区别和联系呢?

3、Java数据结构中,除了数组和链表,似乎也没有什么可以直接和Set进行挂钩呢?

之前的文章中,我们分析了ArrayList的源代码,这次我们同样来分析一下HashSet的源码。

在这之前,先来给大家看一张图:

HashSet的实现原理,与HashMap有何区别和联系

大家看到了什么?HashMap。

是否有疑惑,为什么初始化HashSet会new一个HashMap呢?莫非底层实现就是HashMap

/**
 * 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<>();
}
/**
 * Constructs a new set containing the elements in the specified
 * collection.  The <tt>HashMap</tt> is created with default load factor
 * (0.75) and an initial capacity sufficient to contain the elements in
 * the specified collection.
 *
 * @param c the collection whose elements are to be placed into this set
 * @throws NullPointerException if the specified collection is null
 */
public HashSet(Collection<? extends E> c) {
    map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
    addAll(c);
}
/**
 * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
 * the specified initial capacity and the specified load factor.
 *
 * @param      initialCapacity   the initial capacity of the hash map
 * @param      loadFactor        the load factor of the hash map
 * @throws     IllegalArgumentException if the initial capacity is less
 *             than zero, or if the load factor is nonpositive
 */
public HashSet(int initialCapacity, float loadFactor) {
    map = new HashMap<>(initialCapacity, loadFactor);
}
/**
 * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
 * the specified initial capacity and default load factor (0.75).
 *
 * @param      initialCapacity   the initial capacity of the hash table
 * @throws     IllegalArgumentException if the initial capacity is less
 *             than zero
 */
public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}

在它的构造方法中,全部都是初始化一个HashMap,我们暂且先记下来

而且,从上面的截图中,看到了一个PRESENT变量,这个又是做什么的呢?

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

不知道,没关系,我们也暂且先记下来,继续往下看。

初始化完了,就要来存储数据了,直接去看一下add()方法的代码:

/**
 * Adds the specified element to this set if it is not already present.
 * More formally, adds the specified element <tt>e</tt> to this set if
 * this set contains no element <tt>e2</tt> such that
 * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
 * If this set already contains the element, the call leaves the set
 * unchanged and returns <tt>false</tt>.
 *
 * @param e element to be added to this set
 * @return <tt>true</tt> if this set did not already contain the specified
 * element
 */
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

看到这里,小伙伴们应该已经明白了它为什么在初始化时new一个HashMap了,它直接将我们传进来的数据当做key,而value则是上面提到的PRESENT变量。

这也直接证实了我们上面的猜测:Set的底层实现就是HashMap,充分利用了HashMap中的key不可重复这一特性。

其实到这里基本上已经分析完了,这二者的联系已经一目了然,那我们快速的再将其他常用的方法也都看一遍:

迭代器:

/**
 * 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();
}

Set大小:

/**
 * Returns the number of elements in this set (its cardinality).
 *
 * @return the number of elements in this set (its cardinality)
 */
public int size() {
    return map.size();
}

是否为空:

/**
 * Returns <tt>true</tt> if this set contains no elements.
 *
 * @return <tt>true</tt> if this set contains no elements
 */
public boolean isEmpty() {
    return map.isEmpty();
}

是否包含指定元素:

/**
 * Returns <tt>true</tt> if this set contains the specified element.
 * More formally, returns <tt>true</tt> if and only if this set
 * contains an element <tt>e</tt> such that
 * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
 *
 * @param o element whose presence in this set is to be tested
 * @return <tt>true</tt> if this set contains the specified element
 */
public boolean contains(Object o) {
    return map.containsKey(o);
}

移除元素:

/**
 * Removes the specified element from this set if it is present.
 * More formally, removes an element <tt>e</tt> such that
 * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>,
 * if this set contains such an element.  Returns <tt>true</tt> if
 * this set contained the element (or equivalently, if this set
 * changed as a result of the call).  (This set will not contain the
 * element once the call returns.)
 *
 * @param o object to be removed from this set, if present
 * @return <tt>true</tt> if the set contained the specified element
 */
public boolean remove(Object o) {
    return map.remove(o)==PRESENT;
}

清理Set:

/**
 * Removes all of the elements from this set.
 * The set will be empty after this call returns.
 */
public void clear() {
    map.clear();
}

所有的方法都是通过HashMap来实现的,换句话说,所有的操作都是在HashMap上面进行的,HashSet只是做了一层封装,提供了Key的入口,而将Value直接写死了!

QQ群: 686430774  /  718410762

站长Q: 1347384268

如果文章有帮到你,可以考虑请博主喝杯咖啡!

分享到:

欢迎分享本文,转载请注明出处!

作者:不忘初心

发布时间:2019-03-22

永久地址:https://www.jiweichengzhu.com/article/004de5d98142492c99ef2dfcb29577d5

评论