2009-10-29 84 views

回答

0

看了你的问题更具体:

您不能Set.add添加重复,从Java文档()或你的意思是?:的addAll

添加指定的元素如果此集合尚未出现(可选操作)。更正式地说,如果该集合不包含元素e2(e == null?e2 == null:e.equals(e2)),则将指定的元素e添加到该集合中。如果此集合已包含该元素,则该呼叫将保持集合不变并返回false。结合对构造函数的限制,这可以确保集合永远不会包含重复的元素。

14

实际上,来自java中的源代码最多的Set实现的AFAIK甚至不检查元素是否已被包含。

他们总是在其内部结构上执行add(),该内部结构持有set元素并让该对象处理重复情况。

例如HashSet在内部HashMap上调用put(K,V),它只是插入新对象覆盖旧条目(如果重复)。

10

读一点你的问题我猜你看到奇怪的行为与java.util.HashSet(通常是每个人默认使用)。

Contary到java.util.Set合同有可能得到一个java.util.HashSet同样的对象两次这样的:

import java.util.HashSet; 
import java.util.Set; 

public class SetTest 
{ 
    public static void main(String[] args) 
    { 
    MyClass myObject = new MyClass(1, "testing 1 2 3"); 

    Set<MyClass> set = new HashSet<MyClass>(); 
    set.add(myObject); 

    myObject.setHashCode(2); 
    set.add(myObject); 

    System.out.println(set.size()); // this will print 2. 
    } 

    private static class MyClass 
    { 
    private int hashCode; 
    private String otherField; 

    public MyClass(int hashCode, String otherField) 
    {  
     this.hashCode = hashCode; 
     this.otherField = otherField; 
    } 

    public void setHashCode(int hashCode) 
    { 
     this.hashCode = hashCode; 
    } 

    public boolean equals(Object obj) 
    {  
     return obj != null && obj.getClass().equals(getClass()) && ((MyClass)obj).otherField.equals(otherField); 
    } 

    public int hashCode() 
    { 
     return hashCode; 
    } 
    } 
} 

从@jitter指针,看看源后,你可以看到为什么这会发生。

像@jitter说的,java.util.HashSet在内部使用java.util.HashMap。当散列在第一个和第二个之间变化时加入java.util.HashMap中使用不同的桶并且该对象在该集中两次。

代码示例可能看起来有点受损,但我已经看到这种情况发生在域类中,其中哈希是从可变字段创建的,并且equals方法未与这些字段保持同步。

+2

以修改hashCode()/ equals()结果的方式修改HashSet中的对象会产生未定义的行为。 – 2009-10-29 09:34:12

+1

@Joachim - 确切地说,但并不意味着它不会发生。事实上,流行的IDE生成的equals/hashCode方法通常会导致hashCode随着对象的变化而改变。 – 2009-10-29 09:45:23

+4

如果对象发生了变异,hashcode *应该改变 - 毕竟它需要与equals()一致,所以如果一个对象不再被认为等于它的预突变状态,它就需要改变。这里真正的问题是使用可变对象作为HashMap键; * *建议只使用不可变的对象,否则你打开自己的这种随机性,因为一般来说''hashCode()'*必须*随着可变对象的变化而改变。 – 2009-10-29 10:36:26

2

一个简单的方法来找出这是从源头为您感兴趣的代码查找。

每个JDK已经包含了src.zip其中包含了公共类的源代码,这样你可以只找到HashSet的源代码并看看:)我经常使用Eclipse进行此操作。启动它,创建一个新的Java项目,将JVM设置为已安装的JDK(如果不是,则使用系统默认的JRE,它不包含src.zip),并将Ctrl-Shift-T设置为HashSet。