Java 为什么set不允许重复值,背后使用了什么样的机制?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20870879/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 04:54:16  来源:igfitidea点击:

Why set is not allowed duplicate value, which kind of mechanism used behind them?

java

提问by J.B.Vala

I am new to java, i know set is not allowed duplicate valuebut i don't know why set is not allowed duplicate value, Actually i am doing practically,

我是 Java 新手,我知道set 不允许重复值,但我不知道为什么 set 不允许重复值,实际上我正在做,

Declared one set and add duplicate value but no kind of error is occurring, no compile time error, no run time. why?

声明一组并添加重复值,但没有发生任何错误,没有编译时错误,没有运行时。为什么?

采纳答案by dasblinkenlight

The meaning of "sets do not allow duplicate values" is that when you add a duplicate to a set, the duplicate is ignored, and the set remains unchanged. This does not lead to compile or runtime errors: duplicates are silently ignored.

“集合不允许重复值”的含义是,当您向集合添加重复项时,将忽略重复项,并且该集合保持不变。这不会导致编译或运行时错误:重复项会被静默忽略。

You can tell that a value is a duplicate by checking the result of add, like this:

您可以通过检查 的结果来判断一个值是否重复add,如下所示:

Set<String> testSet = new HashSet<String>();
boolean first = testSet.add("hello");
System.out.println(first);             // Prints "true"
boolean second = testSet.add("hello");
System.out.println(second);            // Prints "false"

回答by T.J. Crowder

Because that's how sets are defined. An element can only exist once in a set, but that doesn't mean trying to add it a second time should be an error. It's just a no-op. This is fairly clear from the documentation, for instance, for Set#add:

因为这就是集合的定义方式。一个元素在一个集合中只能存在一次,但这并不意味着尝试第二次添加它应该是一个错误。这只是一个空操作。这从文档中很清楚,例如,对于Set#add

Adds the specified element to this set if it is not already present (optional operation). ... If this set already contains the element, the call leaves the set unchanged and returns false. In combination with the restriction on constructors, this ensures that sets never contain duplicate elements.

如果指定的元素尚不存在,则将其添加到此集合中(可选操作)。... 如果此集合已包含该元素,则调用将保持该集合不变并返回 false。结合对构造函数的限制,这确保了集合永远不会包含重复的元素。

Among other things, this lets you happily add to sets without worrying, and know that the result will only ever have unique values in it.

除此之外,这让您可以愉快地添加到集合中而不必担心,并且知道结果中只会有唯一的值。

Declared one set and add duplicate value but no any kind of error are occur, no compile time error no run time. Why?

声明一组并添加重复值但没有发生任何类型的错误,没有编译时错误没有运行时。为什么?

Because it's not an error. But note that you did(or could have) received an indication that the value was already present: The addmethod's return value (see link above) tells you: "trueif this set did not already contain the specified element"

因为这不是错误。但请注意,您确实(或可能)收到了该值已经存在的指示:该add方法的返回值(请参阅上面的链接)告诉您:true如果此集合尚未包含指定的元素”

回答by Menios

Set (Oracle Documentation)

设置(Oracle 文档)

A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element. As implied by its name, this interface models the mathematical set abstraction.

不包含重复元素的集合。更正式地说,集合不包含一对元素 e1 和 e2,使得 e1.equals(e2),并且最多包含一个空元素。正如其名称所暗示的那样,该接口对数学集合抽象进行建模。

See: http://docs.oracle.com/javase/7/docs/api/java/util/Set.html

请参阅:http: //docs.oracle.com/javase/7/docs/api/java/util/Set.html

Set (Methematics) - Quoting from wikipedia

集合(数学) - 引自维基百科

In mathematics, a set is a collection of distinct objects, considered as an object in its own right.

在数学中,集合是不同对象的集合,它们本身就是一个对象。

Add Method

添加方法

According to the documentation of the interface, if the element does not exist it is added. Otherwise, nothing changes.

根据接口的文档,如果元素不存在,则添加它。否则,什么都不会改变。

boolean add(E e):

布尔添加(E e):

Adds the specified element to this set if it is not already present (optional operation). If this set already contains the element, the call leaves the set unchanged and returns false.

如果指定的元素尚不存在,则将其添加到此集合中(可选操作)。如果此集合已包含该元素,则调用将保持该集合不变并返回 false

Example Implementation Code: HashSet

示例实现代码:HashSet

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

回答by Andres

Set is not allowed to store duplicated values by definition. If you need duplicated values, use a List. As specified on the documentation of the interface, when you try to add a duplicated value, the method addreturns false, not an Exception.

根据定义,Set 不允许存储重复值。如果您需要重复的值,请使用列表。如接口文档中所述,当您尝试添加重复值时,该方法add返回 false,而不是异常。

http://docs.oracle.com/javase/7/docs/api/java/util/Set.html

http://docs.oracle.com/javase/7/docs/api/java/util/Set.html

回答by BiGYaN

"a set is a collection of distinct objects" ... http://en.wikipedia.org/wiki/Set_%28mathematics%29

“集合是不同对象的集合”... http://en.wikipedia.org/wiki/Set_%28mathematics%29

When you create a set there can be only unique objects in it by definition. Adding same object twice does not change the set as the element/object is already in the set. This is expected behavior. An example where this behavior is useful is when one wants to find unique elements from a collection of elements (i.e. remove duplicates).

当您创建一个集合时,根据定义,其中只能有唯一的对象。添加相同的对象两次不会改变集合,因为元素/对象已经在集合中。这是预期的行为。这种行为很有用的一个例子是当人们想要从一组元素中找到唯一的元素(即删除重复项)。

回答by user3763767

Internally SETstore element using HASHTABLE...HASHTABLEis a structure of Key value pairs..Here what the values passed by the SETis treated as Keys of HASHTABLEInternally. keys are unique cannot be duplicated. That is the reason if you pass any duplicate value it return false and does not added to the SET...

内部使用HASHTABLE 的SET存储元素...... HASHTABLE是一个键值对的结构......这里SET传递的值在内部视为HASHTABLE 的键。键是唯一的,不能重复。这就是原因,如果您传递任何重复值,它会返回 false 并且不会添加到SET...

If the adding element return true it will added into SET...Else it return False, that why it won't give any compilation or runtime error and it wont be added to SET

如果添加元素返回 true 它将添加到SET...否则它返回 False,这就是为什么它不会给出任何编译或运行时错误并且它不会被添加到SET

回答by Balwant Kumar Singh

In addition of above answers, here is why set does not allow duplicate elements:

除了上述答案之外,这就是为什么 set 不允许重复元素的原因:

When you call add(E e), method of set, it internally call put(E, e) method of HashMap which looks something like this :

当你调用 add(E e), set 方法时,它在内部调用 HashMap 的 put(E, e) 方法,它看起来像这样:

 public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

Hence, the element you are adding to set/HashSet, is internally added to Map as a key. As we need to associate some value with the key in so dummy value(new Object()) PRESENT is passed every time (as Map can contain more than one duplicate values ).

因此,您添加到 set/HashSet 的元素在内部作为键添加到 Map。因为我们需要将一些值与键关联,所以每次都传递虚拟值(新对象())PRESENT(因为 Map 可以包含多个重复值)。

Now if you closely examine the return map.put(e, PRESENT)==null;of add(e, E) method. There can be two possibilities :

现在,如果您仔细检查 return map.put(e, PRESENT)==null;of add(e, E) 方法。可能有两种可能性:

  1. if map.put(k,v) returns null ,then map.put(e, PRESENT)==null;will return true and element will be added.
  2. if map.put(k,v) returns old value for the key ,then map.put(e, PRESENT)==null;will return false and element will not be added.
  1. 如果 map.put(k,v) 返回 null ,则 map.put(e, PRESENT)==null;返回 true 并添加元素。
  2. 如果 map.put(k,v) 返回键的旧值, map.put(e, PRESENT)==null;则将返回 false 并且不会添加元素。

Hope this will help in understanding clearly.

希望这将有助于清楚地理解。

回答by Sumanth Varada

Thanks to A2A..

感谢 A2A..

When you pass a duplicate element in the add method of set object, It'll return false and doesn't add it to the set as the element is already present.

当您在 set 对象的 add 方法中传递重复元素时,它将返回 false 并且不会将其添加到 set 中,因为该元素已经存在。

Set<Object> set = new HashSet<Object>();
set.add("test");
set.add("test");

If you look at the implementation of HashSet then it looks like following.

如果您查看 HashSet 的实现,则它如下所示。

public HashSet() {
        map = new HashMap<>();
    }

Means HashSet internally creates an object of HashMap.

表示 HashSet 内部创建了一个 HashMap 对象。

public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

If you look at the add method parameter e, then your passed value (test) will be consider as a key in the map and the PRESENT is the dummy object passed as a value.

如果您查看 add 方法参数 e,那么您传递的值 (test) 将被视为映射中的键,而 PRESENT 是作为值传递的虚拟对象。

HashMap put method returns the following

HashMap put 方法返回以下内容

 1. null, if the key is unique and added to the map
 2. Old value of the key, if key is duplicate

So, when you are adding test element for the first time, HashMap will add the element and return null after that your add method of set will return true. If you add the test element for 2nd time then your HashMap will return Old Value of the Key then in add method of your set will return false as OldValue != null

因此,当您第一次添加测试元素时,HashMap 将添加该元素并返回 null,之后 set 的 add 方法将返回 true。如果您第二次添加测试元素,那么您的 HashMap 将返回键的旧值,然后在您的集合的 add 方法中将返回 false 作为 OldValue != null

Hope it'll be helpful..!!

希望它会有所帮助..!!

回答by Pasii

From the documentation, you can get the following:

从文档中,您可以获得以下信息:

Adds the specified element e to this set if the set contains no element e2 such that (e==null ? e2==null : e.equals(e2)). If this set already contains the element, duplicate is ignored, leaves the set unchanged and returns false. This ensures that sets never contain duplicate elements.

如果该集合不包含元素 e2,则将指定的元素 e 添加到该集合(e==null ? e2==null : e.equals(e2))。如果此集合已包含该元素,则忽略重复项,保持该集合不变并返回 false。这确保了集合永远不会包含重复的元素。

boolean add(E e): The method returns true if the set did not already contain the specified element.

boolean add(E e): 如果集合中尚未包含指定的元素,则该方法返回 true。

It throws the following types of exceptions:

它抛出以下类型的异常:

UnsupportedOperationException- if the add operation is not supported by this set

UnsupportedOperationException- 如果此集合不支持添加操作

ClassCastException- if the class of the specified element prevents it from being added to this set

ClassCastException- 如果指定元素的类阻止它被添加到这个集合

NullPointerException- if the specified element is null and this set does not permit null elements

NullPointerException- 如果指定的元素为 null 并且此集合不允许 null 元素

IllegalArgumentException- if some property of the specified element prevents it from being added to this set

IllegalArgumentException- 如果指定元素的某些属性阻止它被添加到这个集合

回答by ajay tomar

So it is well known fact that Set is not allowing duplicate or equal objects but equality of the objects will be taken care by the programmer by giving the desired implementation of equals() and hashCode() methods. if we are not implementing these two methods set will even allow duplicates also. In hash based collections in java, first hashCode() method called and then equals() method called if required to check the equality of the objects. ,so bottom line is Set is using Map functionality internally and set values are inserted as keys inside this map internally and map is not allowing duplicate keys, You can also check the HashSet.java class from Java API where you can find similar map which is actually doing all the things.

因此,众所周知,Set 不允许重复或相等的对象,但程序员会通过提供所需的 equals() 和 hashCode() 方法实现来注意对象的相等性。如果我们不实现这两个方法 set 甚至会允许重复。在 java 中基于哈希的集合中,首先调用 hashCode() 方法,然后调用 equals() 方法(如果需要检查对象的相等性)。,所以底线是 Set 在内部使用 Map 功能,并且设置值在内部作为键插入该映射中,并且映射不允许重复键,您还可以检查 Java API 中的 HashSet.java 类,您可以在其中找到类似的映射实际上做所有的事情。

Here is small code sample:

这是小代码示例:

class Employee {
    private static int equalsCounter;
    private static int hashCodeCounter;
    private String name;
    private int age;

    public Employee() {
        super();
    }

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Employee [name=" + name + ", age=" + age + "]";
    }

    @Override
    public int hashCode() {
        hashCodeCounter++;
        System.out.println("hashCode() invoked : "+hashCodeCounter+" time");
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        equalsCounter++;
        System.out.println("equals() invoked: "+equalsCounter+" time");
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Employee other = (Employee) obj;
        if (age != other.age)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

public class Main {

    public static void main(String[] args) {
        Set<Employee> mySet  = new HashSet<Employee>();
        Employee e1 = new Employee("aaa", 30);
        Employee e2 = new Employee("aaa", 30);
        Employee e3 = new Employee("aaa", 30);
        mySet.add(e1); // HashCode() called and equals() not called
        mySet.add(e2);// HashCode() called and equals() also called
        mySet.add(e3);// HashCode() called and equals() also called
        System.out.println(mySet.size());
    }
}