【问题标题】:Why set is not allowed duplicate value, which kind of mechanism used behind them?为什么 set 不允许重复值,它们背后使用了什么样的机制?
【发布时间】:2014-01-01 16:53:03
【问题描述】:

我是 java 新手,我知道 set 不允许重复值,但我不知道 为什么 set 不允许重复值,其实我是在做实际的,

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

【问题讨论】:

  • @norlesh:你怎么知道“我知道 java”是指“我是 java 新手”?编辑时最好不要猜测。
  • 您认为另一种解释是什么?
  • @norlesh:我建议不要猜测。除非有人确定,否则应该单独留下 OP 的文本,希望其他人能够理解它。我看到了一些明显错误的猜测,编辑基本上最终(无意中)破坏了这个问题。 (当然还有很多个案例,其中编辑改进/纠正了问题。)
  • {1,2,3} Union {2} = {1,2,3},直接数学。
  • @norlesh:好吧,我们只是不同意。无时无刻不在发生。

标签: java


【解决方案1】:

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

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

【讨论】:

  • 那么,Set 内部工作 HASH TABLE,为什么 Set 不同步?
  • @satzkmr Hash table 是一个通用术语,不一定与java.util.Hashtable 相关。
【解决方案2】:

“集合不允许重复值”的意思是当你向集合添加重复时,重复被忽略,集合保持不变。这不会导致编译或运行时错误:重复项会被忽略。

您可以通过检查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"

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      设置(Oracle 文档)

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

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

      集合(数学)——引用wikipedia

      在数学中,集合是不同对象的集合,其本身被视为一个对象。

      添加方法

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

      布尔加法(E e):

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

      示例实现代码: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;
          }
      

      【讨论】:

        【解决方案5】:

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

        这里是小代码示例:

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

        【讨论】:

          【解决方案6】:

          除了上面的答案,这就是为什么 set 不允许重复元素:

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

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

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

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

          1. 如果 map.put(k,v) 返回 null ,则 map.put(e, PRESENT)==null; 将返回 true 并添加元素。
          2. 如果 map.put(k,v) 返回键的旧值,则 map.put(e, PRESENT)==null; 将返回 false 并且不会添加元素。

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

          【讨论】:

            【解决方案7】:

            感谢 A2A..

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

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

            如果你看一下 HashSet 的实现,它看起来像下面这样。

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

            意味着 HashSet 在内部创建一个 HashMap 对象。

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

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

            HashMap put方法返回如下

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

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

            希望对您有所帮助..!!

            【讨论】:

              【解决方案8】:

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

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

              【讨论】:

                【解决方案9】:

                因为这就是定义集合的方式。一个元素在一个集合中只能存在一次,但这并不意味着再次尝试添加它应该是错误的。这只是一个无操作。从文档中可以清楚地看出这一点,例如Set#add

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

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

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

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

                【讨论】:

                  【解决方案10】:

                  从文档中,您可以获得以下内容:

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

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

                  它会抛出以下类型的异常:

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

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

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

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

                  【讨论】:

                    猜你喜欢
                    • 2017-02-08
                    • 2019-01-21
                    • 1970-01-01
                    • 2013-05-08
                    • 2018-06-22
                    • 2011-05-08
                    • 2021-01-04
                    • 2021-02-07
                    • 1970-01-01
                    相关资源
                    最近更新 更多