假设你有一个类,你创建了一个HashSet来存储这个类的示例,如果你试图添加相等的示例,集合中只会保留一个示例,这是可以的。
但是,如果HashSet中有两个不同的示例,并且您取了一个示例并使其成为另一个示例的精确副本(通过复制字段),则HashSet将包含两个重复的示例。
下面的代码演示了这一点:
public static void main(String[] args)
{
HashSet<GraphEdge> set = new HashSet<>();
GraphEdge edge1 = new GraphEdge(1, "a");
GraphEdge edge2 = new GraphEdge(2, "b");
GraphEdge edge3 = new GraphEdge(3, "c");
set.add(edge1);
set.add(edge2);
set.add(edge3);
edge2.setId(1);
edge2.setName("a");
for(GraphEdge edge: set)
{
System.out.println(edge.toString());
}
if(edge2.equals(edge1))
{
System.out.println("Equals");
}
else
{
System.out.println("Not Equals");
}
}
public class GraphEdge
{
private int id;
private String name;
//Constructor ...
//Getters & Setters...
public int hashCode()
{
int hash = 7;
hash = 47 * hash + this.id;
hash = 47 * hash + Objects.hashCode(this.name);
return hash;
}
public boolean equals(Object o)
{
if(o == this)
{
return true;
}
if(o instanceof GraphEdge)
{
GraphEdge anotherGraphEdge = (GraphEdge) o;
if(anotherGraphEdge.getId() == this.id && anotherGraphEdge.getName().equals(this.name))
{
return true;
}
}
return false;
}
}
上面代码的输出:
是否有办法强制HashSet验证其内容,以便删除在上述场景中创建的可能重复的条目?
一个可能的解决方案是创建一个新的哈希集,并将内容从一个哈希集复制到另一个哈希集,这样新的哈希集就不会包含重复的内容,但我不喜欢这个解决方案。
7条答案
按热度按时间fcipmucu1#
您描述的情况无效。请参阅Javadoc:“当对象是集合中的一个元素时,如果对象值的更改方式会影响等于比较,则不会指定集合的行为。”
nc1teljy2#
为了补充@EJP的答案,如果您改变
HashSet
中的对象使其成为副本(在equals
/hashcode
契约的意义上),实际上会发生什么,散列表数据结构将被破坏。contains
和其他操作)。要么它在错误的哈希链上,要么是因为其他示例在哈希链上出现在它之前。很难预测哪个示例将是可见的......以及它是否仍然可见。Set
约定。当然,从应用程序的Angular 来看,这是非常破碎的。
您可以通过以下任一方法避免此问题:
从正确性和健壮性的Angular 来看,第一种选择显然是最好的。
顺便说一句,用一种通用的方式来“修复”这个问题是非常困难的。在Java中没有普遍的机制来知道......或者被通知......某个元素发生了变化。你可以在一个类接一个类的基础上实现这样的机制,但是它必须被显式地编码(而且代价不菲)。即使你有这样一个机制,你会怎么做?很明显,其中一个对象现在应该从集合中删除......但是是哪一个呢?
ma8fv8wu3#
你是对的,我不认为有任何方法可以防止你所讨论的情况。所有使用哈希和等于的集合都受到这个问题的影响。集合没有通知对象自从被添加到集合以来已经更改。我认为你概述的解决方案是好的。
如果你如此关心这个问题,也许你需要重新考虑你的数据结构。你可以使用不可变的对象,比如说,有了不可变的对象,你就不会有这个问题。
xv8emn3q4#
HashSet
不知道添加对象后其成员属性的更改。如果这对您来说是个问题,则可以考虑使GraphEdge
不可变。例如:在
GraphEdge
不可变的情况下,更改值将导致返回新示例,而不是更改现有示例。dy1byipe5#
一种方法,可用于打印String对象的LinkedList的元素,而不打印任何重复的元素。该方法将LinkedList对象作为输入,然后创建一个新的HashSet对象。然后,该方法遍历输入LinkedList的元素,并将每个元素添加到HashSet中。由于HashSet不允许重复元素,因此这可确保仅将唯一元素添加到HashSet中。
然后,该方法遍历HashSet并将每个元素打印到控制台,元素之间用空格分隔。与printList方法不同,该方法不会在元素列表之前或之后添加任何换行符。它只是打印字符串“Non-duplicates are:“,然后是HashSet的元素。
v7pvogib6#
hashCode用于使用参数对象生成hascode。您将其用作hascode计算的一部分。
尝试将hashCode的实现替换为以下代码:
qco9c6ql7#
你需要在你迭代列表的时候做唯一性检测。创建一个新的HashSet可能看起来不是正确的方法,但是为什么不试试这个呢...也许不使用HashSet开始...