#java #generics #hashmap
#java #общие сведения #хэш-карта
Вопрос:
Я попытался использовать интерфейс в качестве ключа в hashMap
, чтобы иметь 1 карту для нескольких типов ключей. Кажется, работает следующее.
interface Foo {
void function();
}
static class Bar implements Foo {
private int id;
public Bar(int id) {
this.id = id;
}
@Override
public void function() {
System.out.println("this is bar");
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Bar bar = (Bar) o;
return id == bar.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
public static Map<Foo, Integer> map = new HashMap<>();
static class Baz implements Foo {
String name;
public Baz(String name) {
this.name = name;
}
@Override
public void function() {
System.out.println("this is Baz");
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Baz baz = (Baz) o;
return name.equals(baz.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
public static void main(String[] args) {
Bar bar = new Bar(123);
Baz baz = new Baz("some name");
map.put(bar, 10);
map.put(baz, 20);
System.out.println(map.get(bar));
}
В чем я не уверен, так это в том, есть ли какой-то угловой случай, который нарушил бы эту карту?
Есть ли случай, когда использование интерфейса в качестве ключа приведет к сбою? Мог ли я сделать это проще, используя generics?
Комментарии:
1. Звучит немного странно иметь разнородный набор ключей. Что, если у вас есть
class Babble extends Bar
Если кто-то вставил в ваш Bar’s ошибку, они не могут быть равны, даже если у них одинаковый идентификатор. Может быть, использовать, IsAssignableFrom2. @matt: Я полагаю, вы имеете в виду реализацию equals / hashCode и наследование. Хороший момент. Я не стремился к тому, чтобы
Bar
быть расширенным, но, с другой стороны, я не думаю, что смог бы принудительно использовать все классы, используемые в качестве ключей, дляfinal
3. «Есть ли случай, когда использование интерфейса в качестве ключа приведет к сбою?». Конечно, если кто-то реализует ваш интерфейс с неверным хэш-кодом / equals. Или просто изменяемый класс.
Ответ №1:
Единственное, что немного необычно, это то, что методы equals должны сравнивать объекты Bar и Baz. Когда карта содержит только один тип объектов, метод check this.getClass() == that.getClass
in equals никогда не возвращает false . Однако вы реализовали это правильно, так что вам не о чем беспокоиться.
Вы можете получить больше коллизий хэшей, чем ожидаете. Представьте, что у вас есть два класса, которые оба имеют поле идентификатора int и реализуют hashCode с помощью Objects.hash(id)
— теперь объекты разных классов с одинаковым идентификатором имеют одинаковый хэш-код. Если ожидается такой вариант использования, вы можете изменить хэш уникальным для каждого класса способом, например, путем подмешивания к хэшу константы, специфичной для класса:
@Override
public int hashCode() {
return Objects.hash(1, id);
}
...
@Override
public int hashCode() {
return Objects.hash(2, name);
}
Комментарии:
1. Отличная мысль! Теперь я понимаю. Но будет ли работать такая константа, как 1/2? Я имею в виду, не должно ли это быть какое-то простое или случайное число для каждого класса?
2.
Objects.hash
Метод позаботится об этом. Текущая реализация объединяет два значения путем умножения первого значения на простое и добавления второго значения.
Ответ №2:
Теоретически могут возникнуть проблемы с потенциально большим количеством коллизий хэшей, приводящих к снижению производительности из-за различных реализаций hashCode
, поэтому вам нужно быть осторожным и протестировать его с реальными данными. В остальном это допустимый вариант использования.
Комментарии:
1. Разве в наши дни не все генерируют
hashCode
через IDE? Все еще вызывает беспокойство реализация?2. @Jim Если
hashCode
реализация подходит для одного типа, вы можете ожидать хорошего распределения хэшей и хорошейHashMap
производительности. Сложнее рассуждать о распределении хэшей для нескольких типов. В любом случае, если вы не собираетесьHashMap
хранить значительное количество ключей (> 10000) и вашиhashCode
методы в порядке, все должно работать нормально.3.
It's harder to reason about hash distribution for multiple types.
это очень интересно. Не могли бы вы, пожалуйста, пояснить?