Обновление объекта в наборе
Скажем, у меня этот тип в моем приложении:
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).