Java Collections API HashSet删除方法

时间:2011-04-21 01:38:57

标签: java hashset

我在使用Java Collections API时遇到了这个问题。基本上,这是用于实现Kruskal用于查找MST的算法的支持方法。我创建了这个类来实现union / find算法。

我的问题,因为我能够找到一个解决方法,是否有人知道为什么“union”方法中的remove方法不能一致地工作的任何原因。那是在运行时它将删除一些元素而不是其他元素。例如,我实现了这个涉及城市的任务,似乎不喜欢删除一些城市。特别是它偶然发现了几个不同的集合,但总是相同的集合。我想知道它是否是一个对象引用问题,即我是否测试了错误的东西,但我无法解决它。

我知道我的其余工作是正确的,因为我能够用消除元素的循环替换它,并且算法执行得非常完美。但是,可能表现稍差一些。

我想知道是否有人能看到错误。另外我应该注意我从不同的类调用它,但是,调用是使用find方法检索的元素。请注意,find方法必须正常工作,因为只需更改remove方法就可以完成所有工作,即它正在查找并返回相应的对象。

由于

的奥斯卡

/*
 * A constructor for creating a new object of this class.
 */
DisjointSets()
{
    underlying = new HashSet<HashSet<String>>();
}

/*
 * A method for adding a set to this DisjointSets object
 */
void add(HashSet<String> h)
{
    underlying.add(h);
}

/*
 * A method for finding an element in this DisjointSet object.
 */
HashSet<String> find(String s)
{
    // Check each set in the DisjointSets object
    for(HashSet<String> h: underlying)
    {
        if(h.contains(s))
        {
            return h;
        }
    }
    return null;
}

/*
 * A method for combining to subsets of the DisjointSets
 */
void union(HashSet<String> h1, HashSet<String> h2)
{
    System.out.print("CHECK ON DS\n");
    System.out.print("*********************\n");
    System.out.print("H1 is : { ");
    for (HashSet<String> n: underlying) 
    {
        System.out.print("Set is : { ");
        for (String h : n) 
        {
            System.out.print(h + " , ");
        }
        System.out.print("} \n ");
    }
    // Add the objects of h1 to h2
    // DOES NOT WORK CONSISTENTLY
            h1.addAll(h2);
    underlying.remove(h2);
}

}

我用

代替了它
HashSet<HashSet<String>> temp = new HashSet<HashSet<String>>();
        for(HashSet<String> f: underlying)
        {
            if(f != h2)
            {
                temp.add(f);
            }
        }
        underlying = temp;

2 个答案:

答案 0 :(得分:4)

问题在于,当您修改其中一个嵌套HashSet的内容时,会搞砸外部HashSet的内部(因为嵌套HashSet的hashCode()已更改)。为了正确维护这个集合,每当你想修改其中一个嵌套的HashSets时,你必须首先从外部HashSet中删除它,然后重新添加它(如果需要的话)。

(你真的没有提供足够的代码来确定这是否真的是问题,但这是我最好的猜测。)

Set<Set<String>> outerSet = new HashSet<String>();
Set<String> innerSet = new HashSet<String>();

innerSet.add("foo");
outerSet.add(innerSet);

// *** BROKEN ***
innerSet.add("bar");       // <- adding element to innerSet changes result of innerSet.hashCode()
outerSet.remove(innerSet); // <- this may or may not work because outerSet is _broken_
// *** BROKEN ***

// *** CORRECT ***
outerSet.remove(innerSet);
innerSet.add("bar");
// now you can put innerSet back in outerSet if necessary

答案 1 :(得分:0)

跟随@ jtahlborn的回答,AbstractSet.hashCode()的合同说

  

返回此的哈希码值   组。定义了集合的哈希码   是哈希码的总和   集合中的元素。这确保了   s1.equals(s2)意味着   s1.hashCode()== s2.hashCode()for any   根据需要设置两组s1和s2   Object.hashCode的一般合约。

     

此实现枚举结束   set,调用hashCode方法   在集合中的每个元素上,和   将结果加起来。

演示@ jtahlborn答案的代码(这是正确的)

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


public class TestHashSetHashCode {

  public static void main(String[] args)
  {
    Set<String> strings = new HashSet<String>();
    strings.add("one");
    strings.add("two");
    strings.add("three");
    strings.add("four");
    strings.add("five");
    Set<String> test = new HashSet<String>();
    System.out.println("Code "+test.hashCode());
    for (String s : strings) {
      test.add(s);
      System.out.println("Code "+test.hashCode());
    }
  }
}

输出

Code 0
Code 115276
Code 3258622
Code 3368804
Code 113708290
Code 116857384

添加到列表中以便尽可能使用不可变集合的另一个原因。