Ответ 1
Попробуйте поставить точку останова в hashCode().
Если hashCode() из двух объектов в карте возвращает одинаковое число, тогда будут вызываться равенства, чтобы определить, действительно ли они равны.
У меня есть hashmap:
Map<LotWaferBean, File> hm = new HashMap<LotWaferBean, File>();
LotWaferBean lw = new LotWaferBean();
... //populate lw
if (!hm.containsKey((LotWaferBean) lw)) {
hm.put(lw, triggerFiles[l]);
}
Код LotWaferBean
:
@Override
public boolean equals(Object o) {
if (!(o instanceof LotWaferBean)) {
return false;
}
if (((LotWaferBean) o).getLotId().equals(lotId)
&& ((LotWaferBean) o).getWaferNo() == waferNo) {
return true;
}
return false;
}
В моей IDE я поставил точки останова в equals()
, но он никогда не выполняется. Зачем?
Попробуйте поставить точку останова в hashCode().
Если hashCode() из двух объектов в карте возвращает одинаковое число, тогда будут вызываться равенства, чтобы определить, действительно ли они равны.
Только если 2 хэш-кода равны, equals()
будет вызываться во время циклов.
JVM проверяет ведро hashcode этого хэш-кода объекта, если есть больше объектов с одним и тем же хэш-кодом, тогда только метод equals() будет выполнен. И разработчик должен следовать правильному контракту между методами hashCode() и equals().
Только если 2 hashCodes равны, equals() будет вызываться во время клавиш цикла.
это правильный ответ... или почти. Точно так, если два хэш-кода сталкиваются (одинаково гарантируют, что они обязательно столкнутся при правильной импровизации hashmap), только тогда выполняется проверка равенства.
Кстати, ваш равный метод, скорее всего, неверен. В случае переопределения LotWaferBean
ваш метод equals
будет принимать экземпляр подкласса, но будет ли ваш подкласс также выполнен?
Лучше читать:
@Override
public boolean equals(Object o) {
if (o == null || o.getClass() != getClass()) { // << this is important
return false;
}
final LotWaferBean other = (LotWaferBean)o;
return other.getLotId().equals(lotId)
&& other.getWaferNo() == waferNo);
}
Как отметил Абимаран Кугатсан, реализация HashMap использует хэш-ведра для эффективного поиска ключей и использует только equals() для сравнения ключей в соответствующем хэш-ведре с данным ключом. Стоит отметить, что ключи назначаются хэш-ковши, когда они добавляются в HashMap. Если вы измените ключи в HashMap после их добавления, таким образом, чтобы изменить их хэш-код, они не будут в правильном хэш-ведре; и попытка использовать соответствующий ключ для доступа к карте найдет правильный хэш-ведро, но он не будет содержать измененный ключ.
class aMutableType {
private int value;
public aMutableType(int originalValue) {
this.value = originalValue;
}
public int getValue() {
return this.value;
}
public void setValue(int newValue) {
this.value = newValue;
}
@Override
public boolean equals(Object o) {
// ... all the normal tests ...
return this.value == ((aMutableType) o).value;
}
@Override
public int hashCode() {
return Integer.hashCode(this.value);
}
}
...
Map<aMutableType, Integer> aMap = new HashMap<>();
aMap.put(new aMutableType(5), 3); // puts key in bucket for hash(5)
for (aMutableType key : new HashSet<>(aMap.keySet()))
key.setValue(key.getValue()+1); // key 5 => 6
if (aMap.containsKey(new aMutableType(6))
doSomething(); // won't get here, even though
// there a key == 6 in the Map,
// because that key is in the hash-bucket for 5
Это может привести к некоторому довольно странному поведению. Вы можете установить точку останова непосредственно перед theMap.containsKey(theKey) и увидеть, что значение ключа совпадает с ключом в Map, и все же ключ equals() не будет вызываться, а containsKey() вернет false.
Как отмечено здесь fooobar.com/info/218987/..., на самом деле есть предупреждение JavaDoc для Map относительно использования изменяемых типов для ключей. Типы не-хэш-карт не будут иметь этой конкретной проблемы, но могут иметь другие проблемы, когда ключи изменены на месте.