Я реализовал алгоритм Apriori. он работает очень хорошо, но у меня возникла странная проблема: я определил класс Rule для поддержания сгенерированных правил.
Вот:
public class Rule
{
private Set<Integer> left;
private Set<Integer> right;
private LookupArtist lookupArtist;
public Rule(LookupArtist lookupArtist){
left = new HashSet<>();
right = new HashSet<>();
this.lookupArtist = lookupArtist;
}
@Override
public boolean equals(Object another){
Rule rule = (Rule) another;
if(this.left.equals(rule.getLeft()) && this.right.equals(rule.getRight()))
return true;
else
return false;
}
@Override
public String toString(){
/* print the object */
}
public void addToLeft(Integer toAdd){
left.add(toAdd);
}
public void addToRight(Integer toAdd){
right.add(toAdd);
}
public Set<Integer> getLeft(){
return left;
}
public Set<Integer> getRight(){
return right;
}
}
Я также equals()
метод equals()
по-другому, просто чтобы попробовать:
@Override
public boolean equals(Object another){
Rule rule = (Rule) another;
boolean flag = true;
for(Integer artist : left){
if(flag)
if(!rule.left.contains(artist))
flag=false;
}
if(flag)
for(Integer artist : right){
if(flag)
if(!rule.right.contains(artist))
flag=false;
}
return flag;
}
Объект LookupArtist используется для сопоставления целых чисел с некоторыми строками.
Проблема в том, что когда я печатаю правила, я обнаружил, что некоторые правила появляются два раза. Я также нашел в режиме отладки некоторые реплицированные правила, поэтому это не проблема печати. Правила сохраняются на карте следующим образом:
static Map<Rule, Float> rules;
.
.
.
Rule rule = new Rule(lookupArtist);
for(int j=0;j<i;j++){
rule.addToLeft(a[j]);
}
for(int j=i;j<a.length;j++){
rule.addToRight(a[j]);
}
if(!rules.containsKey(rule)){
rules.put(rule, getRuleConfidence(rule));
}
Любая идея, где проблема может быть?
Вы всегда должны переопределять hashCode
при переопределении equals
и наоборот.
Добавьте что-то вроде этого в свой класс Rule
:
@Override
public int hashCode() {
return left.hashCode()
^ right.hashCode()
^ lookupArtist.hashCode();
}
Вот хороший ответ, объясняющий, почему важно переопределить оба.
Кроме того, ваш метод equals может быть записан как
@Override
public boolean equals(Object another){
Rule rule = (Rule) another;
return left.equals(rule.left)
&& right.equals(rule.right)
&& lookupArtist.equals(rule.lookupArtist);
}
Заключительное замечание: Ваша другая попытка реализации равенства не симметрична, т. rule1.equals(rule2)
Это не тот случай, когда rule1.equals(rule2)
тогда и только тогда, когда rule2.equals(rule1)
. Это нарушение договора равных.
При использовании HashSet
для хранения объектов класса, у которого есть обычная equals
реализация, вы должны иметь соответствующую специальную реализацию для hashCode
.
Если два объекта равны (в соответствии с пользовательской equals
реализацией ), they must have the same
хэш-код . In the code you posted, я don't see an overriding of
. In the code you posted, я don't see an overriding of
hashCode in the
Rule.
Когда вы добавляете экземпляр в HashSet
, метод hashCode
используется для определения индекса в хеш-таблице, в которой будет храниться экземпляр. Затем связанный список экземпляров, хранящихся в этом индексе, повторяется, чтобы увидеть, существует ли экземпляр. При повторении этого списка используется значение equals
. Если два одинаковых объекта сопоставляются hashCode
с разными индексами в HashSet
, дублирование не будет обнаружено, поскольку они будут храниться в отдельных связанных списках.
Это указано в Джавадоке equals
:
* Note that it is generally necessary to override the <tt>hashCode</tt>
* method whenever this method is overridden, so as to maintain the
* general contract for the <tt>hashCode</tt> method, which states
* that equal objects must have equal hash codes.
И в Javadoc hashCode
:
* <li>If two objects are equal according to the <tt>equals(Object)</tt>
* method, then calling the <code>hashCode</code> method on each of
* the two objects must produce the same integer result.
И где ваш метод hashCode()? Это тоже очень важно :)