LinkedHashSets的HashSet不删除具有多个元素的LinkedHashSet

HashSet of LinkedHashSets does not delete LinkedHashSet with multiple elements

我正在学习数据结构和算法,并尝试在 Java 中实现不相交集数据结构。这是我执行相同操作的代码-

import java.util.*;

public class DisjointSet<T> {
    Set<LinkedHashSet<T>> allSets;

    DisjointSet()   {
        allSets = new HashSet<LinkedHashSet<T>>();
    }

    public void makeSet(T t)    {
        Iterator itr = allSets.iterator();
        while (itr.hasNext())   {
            LinkedHashSet set = (LinkedHashSet) itr.next();
            if (set.contains(t))    {
                return;
            }
        }

        LinkedHashSet<T> set = new LinkedHashSet<T>();
        set.add(t);

        allSets.add(set);
    }

    public T findSet(T t)   {       
        Iterator itr = allSets.iterator();
        while (itr.hasNext())   {
            LinkedHashSet set = (LinkedHashSet) itr.next();
            if (set.contains(t))    {
                Iterator itr1 = set.iterator();
                T t1 = (T) itr1.next();
                return t1;
            }
        }

        return null;
    }

    public void union(T t1, T t2)   {
        LinkedHashSet<T> set1 = null, set2 = null;

        Iterator itr = allSets.iterator();
        while (itr.hasNext())   {
            LinkedHashSet set = (LinkedHashSet) itr.next();
            if (set.contains(t1))   {
                set1 = (LinkedHashSet<T>) set;
                System.out.println("Got set1:: " + set1);
            } else if (set.contains(t2))    {
                set2 = (LinkedHashSet<T>) set;
                System.out.println("Got set2:: " + set2);
            }
        }

        if (null != set1)   {
            System.out.println("Adding set2 to set1");
            set1.addAll(set2);

            if (null != set2)   {
                System.out.println("Removing set2");
                System.out.println(allSets.remove(set2));
            }
        }
    }

    public void viewAllSets()   {
        System.out.println(this.allSets);
    }
}

这是我正在 运行 测试我的实现的代码-

public class DisjointTest   {   
    public static void main(String [] args) {
        DisjointSet<Integer> dsets = new DisjointSet<Integer>();

        dsets.makeSet(30);
        dsets.makeSet(600);
        dsets.makeSet(20);
        dsets.makeSet(25);
        dsets.makeSet(90);
        dsets.makeSet(100);
        dsets.makeSet(1);

        dsets.viewAllSets();
        System.out.println();

        System.out.println(dsets.findSet(25));

        dsets.union(20, 25);
        dsets.viewAllSets();

        System.out.println();

        System.out.println(dsets.findSet(25));

        dsets.union(1, 100);
        dsets.viewAllSets();

        System.out.println();

        dsets.union(20, 100);
        dsets.viewAllSets();

        System.out.println(dsets.findSet(100));

        System.out.println();

        dsets.union(30, 90);
        dsets.viewAllSets();

        System.out.println();

        dsets.union(1, 90);
        dsets.viewAllSets();
    }   
}

当我尝试将一个集合与另一个集合合并时,比如 set2,它有 2 个或更多元素,这些集合被正确合并,但即使在调用 allsets.remove(set2)

但是,如果要合并集合,即; set2,只有 1 个元素,allSets.remove(set2) 成功从 sets 集合中删除 set2。

这是我的代码输出,它证实了我的问题-

[[1], [100], [20], [25], [600], [90], [30]]

25
Got set1:: [20]
Got set2:: [25]
Adding set2 to set1
Removing set2
true
[[1], [100], [20, 25], [600], [90], [30]]

20
Got set1:: [1]
Got set2:: [100]
Adding set2 to set1
Removing set2
true
[[1, 100], [20, 25], [600], [90], [30]]

Got set2:: [1, 100]
Got set1:: [20, 25]
Adding set2 to set1
Removing set2
false
[[1, 100], [20, 25, 1, 100], [600], [90], [30]]
1

Got set1:: [20, 25, 1, 100]
Got set2:: [90]
Adding set2 to set1
Removing set2
true
[[1, 100], [20, 25, 1, 100, 90], [600], [30]]

我无法理解为什么 HashSet.remove(LinkedHashSet) 无法删除包含多个元素的 LinkedHashSet,但成功删除包含 1 个元素的 LinkedHashSet

我们将不胜感激任何帮助。 非常感谢。

您错过了使用集合的一个关键点:您存储在集合中的值不得修改(至少不能以改变其相等性的方式)。

如果您修改存储在 HashSet 中的值,并且该修改更改了值的 hashCode,则无法再在集合中找到该值,因为它不在与其 hashCode 对应的存储桶中没有了。

当一个set与另一个set合并时,你改变了它的hashCode,从而彻底破坏了整个数据结构的正确性。

示例:

  1. 创建一个包含1的LinkedHashSet inner1。hashCode为5
  2. 存放在outerSet中
  3. outerSet 将 inner1 存储在 bucket 5 中,因为它的 hashCode 是 5
  4. 将 2 添加到 inner1。现在它的 hashCode 变成了 3.
  5. 从外部集中删除内部 1。
  6. outerSet获取inner1的hashCode找到inner1所在的桶:3
  7. outerSet 尝试在桶 3 中查找 inner1。什么也没找到,因为它存储在桶 5 中。

请注意,TreeSet 也是如此。 TreeSets 通过构建值树来工作:如果 A 小于根,则它转到左分支。如果你修改 A 并且它变得比根大,TreeSet 将尝试在树的错误分支中找到它。

至于其他人已经回答的理论基础。

就解决方案而言,使用良好的 ol 向量并避免花哨的 smanchy hashsets - 如果你使用 hashsets,你在数学上理解的集合的概念就会被破坏

import java.util.*;

public class DisjointSet<T> {
Vector allSets;

DisjointSet()   {
    allSets = new Vector();
}

public void makeSet(T t)    {
    Iterator itr = allSets.iterator();
    while (itr.hasNext())   {
        Vector set = (Vector) itr.next();
        if (set.contains(t))    {
            return;
        }
    }

    Vector set = new Vector();
    set.add(t);

    allSets.add(set);
}

public T findSet(T t)   {       
    Iterator itr = allSets.iterator();
    while (itr.hasNext())   {
        Vector set = (Vector) itr.next();
        if (set.contains(t))    {
            Iterator itr1 = set.iterator();
            T t1 = (T) itr1.next();
            return t1;
        }
    }

    return null;
}

public void union(T t1, T t2)   {
    Vector set1 = null, set2 = null;

    Iterator itr = allSets.iterator();
    while (itr.hasNext())   {
        try {
        Vector set = (Vector) itr.next();
        if (set.contains(t1))   {
            set1 = (Vector) set;
            System.out.println("Got set1:: " + set1);
        } else if (set.contains(t2))    {
            set2 = (Vector) set;
            System.out.println("Got set2:: " + set2);
        }
        }
        catch(Exception e) { e.printStackTrace(); }
    }

    if (null != set1)   {
        System.out.println("Adding set2 to set1");
        set1.addAll(set2);

        if (null != set2)   {
            System.out.println("Removing set2");
    viewAllSets();
            System.out.println(allSets.contains(set2)+" "+allSets.remove(set2));
        }
    }
}

public void viewAllSets()   {
    System.out.println(this.allSets);
}

public static void main(String [] args) {
    DisjointSet<Integer> dsets = new DisjointSet<Integer>();

    dsets.makeSet(30);
    dsets.makeSet(600);
    dsets.makeSet(20);
    dsets.makeSet(25);
    dsets.makeSet(90);
    dsets.makeSet(100);
    dsets.makeSet(1);

    dsets.viewAllSets();
    System.out.println();

    System.out.println(dsets.findSet(25));

    dsets.union(20, 25);
    dsets.viewAllSets();

    System.out.println();

    System.out.println(dsets.findSet(25));

    dsets.union(1, 100);
    dsets.viewAllSets();

    System.out.println();

    dsets.union(20, 100);
    dsets.viewAllSets();

    System.out.println(dsets.findSet(100));

    System.out.println();
}   

}