当我将重复元素添加到 HashSet 时会发生什么?旧元素是否被覆盖?

What happens when I add duplicate element into HashSet? Is old element overwritten or not?

假设我想在 HashSet 中添加重复元素。我们知道这是不允许的。所以我遇到了 2,比方说 理论

在这个 link 的 'PROPERTIES, number 2' 下,它说:

HashSet does not allow duplicate elements. If you try to insert a duplicate element, older element will be overwritten

但是当我检查 IDE 中提供给我的文档时,在方法 add() 中,它指出:

* If this set already contains the element, the call leaves the set
* unchanged and returns {@code false}.

那么它是覆盖(替换)旧元素还是保持集合不变并且returns false? :) 我是不是很讨厌他们说的恰恰相反?

任何理论都可以通过查看源代码来验证。

例如对于JDK8,HashSet::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;
    }

这里mapHashMap的一个实例,所以我们应该看看HashMap::put:

 /**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     *
     * @param key key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

putVal 的实现包含此代码:

            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }

执行下面的程序,你会看到不允许重复的元素:

import java.util.HashSet;

public class Test {
    public static class Dummy{
         String s1;
         String s2;
        public Dummy(String s1, String s2) {
            super();
            this.s1 = s1;
            this.s2 = s2;
        }
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((s1 == null) ? 0 : s1.hashCode());
            return result;
        }
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Dummy other = (Dummy) obj;
            if (s1 == null) {
                if (other.s1 != null)
                    return false;
            } else if (!s1.equals(other.s1))
                return false;
            return true;
        }
        @Override
        public String toString() {
            return "Dummy [s1=" + s1 + ", s2=" + s2 + "]";
        }
    }
    
    
    public static void main(String[] args) {
        HashSet<Object>hashSet =new HashSet<>();
        Object o1 = new Dummy("a","b");
        Object o2 = new Dummy("a","c");
        System.out.println(o1.equals(o2));
        hashSet.add(o1);
        System.out.println(hashSet);
        hashSet.add(o2);
        System.out.println(hashSet);
    }

}

IDE 正在显示 Set.add 方法的官方规范(javadocs)的摘录。

IDE 是正确的。相信官方文档,而不是一些“漂亮”的网站。


But how could people that made that site make such a big mistake then?

永远记住,像您发现的网站这样的网站的主要动机是通过您的页面浏览量赚钱。他们通常更关注“搜索引擎优化”,而不是 material.


But the problem is those "random" sites sometimes make 'prettier' explanation of some concepts. Official docs are sometimes too hard to follow for me as a beginner.

那么哪个更好?一些容易阅读但错误的东西?或者准确的东西?

在这种情况下,你不能说官方文档难懂。您自己能够看到官方文档和 3rd-party 网站上的文本之间的矛盾。

我建议我们始终尝试先阅读官方 javadoc,并始终相信它优于任何其他来源...包括 Whosebug 答案!唯一的事情是更权威1 是 OpenJDK 源代码。


1 - 即使这样也值得商榷。一方面,代码决定了实际发生了什么。另一方面,代码可能会从一个版本更改为另一个版本。因此,依赖 javadoc 中未 指定 的行为可能会导致可移植性问题。