Обновление объекта в наборе

Скажем, у меня этот тип в моем приложении:

public class A {
  public int id;
  public B b;

  public boolean equals(Object another) { return this.id == ((A)another).id; }
  public int hashCode() { return 31 * id; //nice prime number }
}

и a Set<A > . Теперь у меня есть объект типа A и хочу сделать следующее:

  • Если мой A находится внутри набора, обновите его поле b для соответствия моему объекту.
  • Еще, добавьте его в набор.

Таким образом, проверка того, находится ли там, достаточно проста (contains), и добавление в набор тоже легко. Мой вопрос таков: как мне получить дескриптор для обновления объекта внутри? Интерфейс Set не имеет метода get, и лучше всего я мог подумать об удалении объекта в наборе и добавлении моего. другой, еще хуже, альтернативой является перемещение набора с помощью итератора, чтобы попытаться найти объект.

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

Yuval = 8 -)

РЕДАКТИРОВАТЬ: Спасибо всем за ответы... К сожалению, я не могу "принять" лучшие ответы здесь, те, которые предлагают использовать Map, потому что изменение типа коллекции радикально для этой цели было бы немного экстремально (эта коллекция уже отображается через Hibernate...)

Ответ 1

Так как Set может содержать только один экземпляр объекта (как определено его методами equals и hashCode), просто удалите его, а затем добавьте его. Если он уже был, другой будет удален из Set и заменен тем, который вы хотите.

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

Ответ 2

Вы действительно хотите использовать Map<Integer,A>, а не Set<A>.

Затем сопоставьте идентификатор (хотя он также хранится в A !) С объектом. Таким образом, хранение нового это:

A a = ...;
Map<Integer,A> map = new HashMap<Integer,A>();
map.put( a.id, a );

Ваш полный алгоритм обновления:

public static void update( Map<Integer,A> map, A obj ) {
  A existing = map.get( obj.id );
  if ( existing == null )
     map.put( obj.id, obj );
  else
     existing.b = obj.b;
}

Тем не менее, это может быть еще проще. Я предполагаю, что у вас больше полей, чем в A, что вы дали. Если это не так, то просто использование Map<Integer,B> на самом деле то, что вы хотите, тогда оно сворачивается в ничто:

Map<Integer,B> map = new HashMap<Integer,B>();
// The insert-or-update is just this:
map.put( id, b );

Ответ 3

Я не думаю, что вы можете сделать это проще, чем использовать remove/add, если вы используете Set.

    set.remove(a);
    set.add(a);

Если найдено совпадение A, оно будет удалено, а затем вы добавите новый, вам даже не понадобится условие if (set.contains(A)).

Если у вас есть объект с идентификатором и обновленное поле и, вам все равно не нужны какие-либо другие аспекты этого объекта, просто выбросьте его и замените.

Если вам нужно сделать что-нибудь еще для A, которое соответствует этому идентификатору, вам придется пройти через набор, чтобы найти его или использовать другой Контейнер (например, карту, предложенную Джейсоном).

Ответ 4

Никто еще не упомянул об этом, но основать hashcode или equals на изменяемом свойстве является одной из тех действительно больших вещей, которые вам не следует делать. Не отчаивайтесь с идентификатором объекта после того, как вы покинете конструктор, - это значительно увеличивает ваши шансы на то, что у вас будут проблемы с ошибками в дороге. Даже если вы не попадаете с ошибками, учетная работа позволяет убедиться, что вы всегда должным образом обновляете любые структуры данных, которые полагаются на равные и согласованные хэш-коды, намного превосходят любые предполагаемые преимущества, позволяющие просто изменить идентификатор объект при запуске.

Вместо этого я настоятельно рекомендую вам передать идентификатор через конструктор, и если вам нужно его изменить, создайте новый экземпляр A. Это заставит пользователей вашего объекта (включая вас) правильно взаимодействовать с классами коллекции (и многие другие), которые полагаются на неизменное поведение на равных и хэш-кодах.

Ответ 5

Как насчет карты <A,A > Я знаю, что это избыточно, но я считаю, что это принесет вам поведенческое поведение. На самом деле мне бы хотелось увидеть, что Set имеет на нем метод get (Object o).

Ответ 6

Это немного вне области видимости, но вы забыли перекомпилировать hashCode(). Когда вы переопределяете equals, пожалуйста, переопределите hashCode(), даже в примере.

Например; contains(), скорее всего, пойдет не так, если у вас есть реализация HashSet Set, поскольку HashSet использует хэш-код объекта для поиска ведра (число, которое не имеет ничего общего с бизнес-логикой) и только равно() элементы внутри этого ковш.

public class A {
  public int id;
  public B b;
  public int hashCode() {return id;} // simple and efficient enough for small Sets 
  public boolean equals(Object another) { 
    if (object == null || ! (object instanceOf A) ) {
      return false;
    }
    return this.id == ((A)another).id; 
   }
}
public class Logic {
  /**
   * Replace the element in data with the same id as element, or add element
   * to data when the id of element is not yet used by any A in data. 
   */
  public void update(Set<A> data, A element) {
    data.remove(element); // Safe even if the element is not in the Set
    data.add(element); 
  }
}

РЕДАКТИРОВАТЬ. Правильно задано, что Set.add не перезаписывает существующий элемент, а только добавляет, если элемент еще не находится в коллекции (с "is" реализовано равным)

Ответ 7

Возможно, вы захотите создать декоратор под названием ASet и использовать внутреннюю карту в качестве структуры данных поддержки

class ASet {
 private Map<Integer, A> map;
 public ASet() {
  map = new HashMap<Integer, A>();
 }

 public A updateOrAdd(Integer id, int delta) {
   A a = map.get(a);
   if(a == null) {
    a = new A(id);
    map.put(id,a);
   }
   a.setX(a.getX() + delta);
 }
}

Вы также можете взглянуть на API Trove. Хотя это лучше для производительности и для учета того, что вы работаете с примитивными переменными, он очень хорошо раскрывает эту функцию (например, map.adjustOrPutValue(key, initialValue, deltaValue).