HashMap получает ключ, учитывая, что у меня уже есть ключ - PullRequest
0 голосов
/ 29 февраля 2020

Это звучит как очень странный запрос, но у меня есть HashMap пользовательского класса. Я переопределил методы equals и hashCode, чтобы сосредоточиться только на определенных полях, чтобы я мог потянуть ключ, если он равен новому ключу с такими же определенными полями. В этом случае я хочу заменить другие поля новыми значениями. Структура выглядит примерно так:

public class ExampleClass() {
   int field1;
   int field2;

   <insert constructor here with field1 and field2>

   @Override
   public boolean equals(Object obj) { // Only return true if field1 is equal
      ...
      return (this.field1 == obj.field1);
   }
}

Итак, я использую это так:

HashMap<ExampleClass, int> hmap = new HashMap<>();
while(true) {
   ...
   ExampleClass oldObject = new ExampleClass(1, 2);
   ExampleClass newObject = new ExampleClass(1, 5);

   hmap.put(oldObject, 10);
   if(hmap.contains(newObject)) {
      // Get field1 of old object and change it
   }
}

Это был плохой пример, но я просто хочу иметь возможность получить ключевой объект пара ключ-значение в HashMap, учитывая, что у меня есть ключ, чтобы я мог изменить ключ. Как бы я это сделал?

Редактировать: Моя функция хэш-кода.

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + 
            ((this.srcVertex.getVertexData().getID() == null) ? 0 : this.srcVertex.getVertexData().getID().hashCode());
    result = prime * result + 
            ((this.targetVertex.getVertexData().getID() == null) ? 0 : this.targetVertex.getVertexData().getID().hashCode());
    return result;
  }

Ответы [ 3 ]

1 голос
/ 29 февраля 2020

Насколько я понимаю, у вас есть ключевой объект в HashMap, и вы хотите использовать "равный" ключевой объект для получения ключа, а не значения, с которым он связан. В HashMap нет метода для этого, и это несколько нарушает идею о том, что два объекта «равны», если вам действительно важно, какой из двух равных объектов вы получите.

Я думаю, что это сделает больше смысл делать это по-другому:

  • Написать новый класс ExampleKey только с полями, которые вы хотите использовать в методах equals / hashCode для целей HashMap , Этот класс должен переопределять equals и hashCode, используя эти поля, и он должен быть неизменным (поведение не определено, если ключ ha sh может измениться, пока он находится в HashMap).
  • Give ExampleClass getKey() метод, который возвращает объект ExampleKey для текущего объекта. Вероятно, здесь проще использовать состав , чтобы ExampleClass не дублировал эти поля.
  • Теперь имеется два HashMaps: Map<ExampleKey, Integer> для фактического отображения, которое вы хотите store и отдельное Map<ExampleKey, ExampleClass> хранилище объекта, который в противном случае использовался бы в качестве ключа в другом HashMap.

Пример использования:

Map<ExampleKey, Integer> actualMapping = new HashMap<>();
Map<ExampleKey, ExampleClass> objsUsed = new HashMap<>();
while(true) {
   // ...
   ExampleClass oldObject = new ExampleClass(1, 2);
   ExampleClass newObject = new ExampleClass(1, 5);

   // always update both maps together, to ensure valid state
   actualMapping.put(oldObject.getKey(), 10);
   objsUsed.put(oldObject.getKey(), oldObject);

   // ...

   ExampleClass objUsed = objsUsed.get(newObject.getKey());
   if(objUsed != null) {
        // objUsed == oldObject here
   }
}

Если вы не ' не заботясь о философии того, что означает «равный», тогда вы можете применить это же решение без класса ExampleKey или метода getKey; просто используйте сами объекты, то есть objsUsed будет иметь тип Map<ExampleClass, ExampleClass>, и он всегда будет привязывать объект к себе. Но я думаю, что если вы сделаете это, читатели вашего кода будут ломать голову над вопросом, почему вы отображаете объекты на себя.

0 голосов
/ 29 февраля 2020

Map s в Java следует указывать в значениях, которые equals определены для всех их «основных свойств». Я полагаю, что большинство библиотек коллекций работают так, и единственный пример, который приходит на ум, - это пример Telescript от General Magi c.

Итак, Map определено для типа только этих свойств , field1 int (Integer) в этом случае. Поместите остальную информацию в значение записи карты. Вполне возможно, это новый класс.

Map<Integer, ValueClass> map;

, где

public final class ValueClass {
    private int someValue;
    private ExampleClass exmaple;
    ...

Если вы настойчивы, вы хотите найти ключ, который, я полагаю, вам не нужен. Существуют различные способы сделать это, что-то вроде:

Optional<ExampleClass> found = map.keySet().stream()
    .firstThat(k -> k.field1() == target);
found.ifPresent(key -> {
   Integer value = hmap.remove(key);
   // update key.
   hmap.put(key, value);
});

Или версия старой школы (выглядит лучше для меня, но не так круто):

for (ExampleClass key : map.keySet()) {
    if (key.field1() == target) {
        Integer value = hmap.remove(key);
        // update key.
        hmap.put(key, value);
    }
}

Использование Iterator или, возможно, набор входных данных лучше, так как он избегает второго из трех поисков, но я оставлю это как упражнение.

0 голосов
/ 29 февраля 2020

Вы забыли определить метод hashCode (). Без этого сохранение объекта в качестве ключа в HashMap не работает.

ОБНОВЛЕНИЕ:

Если srcVertex равен field1, а targetVertex - field2, то ваш метод hashCode () неверен. Если equals () сравнивает srcVertex, то hashCode () должен использовать только srcVertex, а не targetVertex.

Правило таково: если 2 объекта равны, то их коды ha sh должны быть равны.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...