Java: получение элемента из HashSet
Надеюсь, кто-то может объяснить, почему я не могу извлечь элемент из HashSet
.
Рассмотрим мой HashSet
, содержащий список MyHashObjects
с правильными методами hashCode()
и equals()
.
То, что я надеялся сделать, это построить сам MyHashObject
и установить соответствующие свойства хэш-кода для определенных значений.
Я могу запросить HashSet
, чтобы увидеть, есть ли в нем "эквивалентные" объекты с помощью метода contains()
.
Поэтому, даже если contains()
возвращает true для 2 объектов, они могут быть не ==
true.
Почему не существует метода get()
, аналогичного тому, как работает contains()
?
Заинтересованы в понимании мышления, лежащего в основе этого решения API.
Ответы
Ответ 1
Если вы знаете, какой элемент вы хотите получить, то у вас уже есть элемент. Единственный вопрос для ответа Set
, заданного элементом, заключается в том, является ли оно contains()
или нет.
Если вы хотите использовать итератор над элементами, просто используйте Set.iterator()
.
Похоже, то, что вы пытаетесь сделать, это обозначить канонический элемент для класса эквивалентности элементов. Вы можете использовать Map<MyObject,MyObject>
для этого. См. этот вопрос SO или этот для обсуждения.
Если вы действительно решили найти элемент, который .equals()
ваш исходный элемент с ограничением, которое вы ДОЛЖНЫ использовать HashSet
, я думаю, что вы застряли в нем итерации по нему и проверили equals()
самостоятельно. API не позволяет вам что-то захватывать хеш-кодом. Таким образом, вы можете сделать:
MyObject findIfPresent(MyObject source, HashSet<MyObject> set)
{
if (set.contains(source)) {
for (MyObject obj : set) {
if (obj.equals(source))
return obj;
}
}
return null;
}
Грубая сила и O (n) уродливые, но если это то, что вам нужно сделать...
Ответ 2
Похоже, вы пытаетесь использовать хэш-код в качестве ключа на карте (это то, что HashSets делает за кулисами). Вы можете просто сделать это явно, объявив HashMap<Integer, MyHashObject>
.
Для HashSets нет get
, потому что обычно объект, который вы передадите методу get
в качестве параметра, - это тот же объект, который вы вернетесь.
Ответ 3
Вы можете HashMap<MyHashObject,MyHashObject>
вместо HashSet<MyHashObject>
.
Вызов ContainsKey()
на вашем "восстановленном" MyHashObject
будет сначала hashCode()
-check collection, и если будет удалено дублирующее хэш-код, наконец equals()
-check ваш "реконструированный" против оригинала, на котором вы может извлекать оригинал с помощью get()
Это O (1), но недостатком является то, что вам, вероятно, придется переопределить методы equals()
и hashCode()
.
Ответ 4
Если вы знаете порядок элементов в Установить, вы можете получить их, преобразов Установить в Массив. Что-то вроде этого:
Set mySet = MyStorageObject.getMyStringSet();
Object[] myArr = mySet.toArray();
String value1 = myArr[0].toString();
String value2 = myArr[1].toString();
Ответ 5
Если я точно знаю, что в моем приложении объект не используется в поиске в любой из структуры списка или хеш-данных и не используется метод equals в другом месте, кроме тех, которые косвенно используются в структуре хэш-данных при добавлении. Рекомендуется ли обновлять существующий объект в методе equals. См. Приведенный ниже код. Если я добавлю этот bean в HashSet, я могу выполнить агрегацию групп на соответствующем объекте на ключе (id). Таким образом, я могу выполнять функции агрегации, такие как sum, max, min,.... Если это не рекомендуется, пожалуйста, не стесняйтесь делиться со мной своими мыслями.
public class MyBean {
String id;
String name;
double amountSpent;
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object obj) {
if(obj!=null && obj instanceof MyBean ) {
MyBean tmpObj = (MyBean) obj;
if(tmpObj.id!=null && tmpObj.id.equals(this.id)) {
tmpObj.amountSpent += this.amountSpent;
retuen true;
}
}
return false;
}
}
Ответ 6
Идея о том, что вам нужно получить ссылку на объект, содержащийся внутри объекта Set, является общей. Его можно архивировать двумя способами:
1. Используйте HashSet, как вы хотели, затем:
public Object getObjectReference(HashSet<Xobject> set, Xobject obj) {
if (set.contains(obj)) {
for (Xobject o : set) {
if (obj.equals(o))
return o;
}
}
return null;
}
Для этого подхода к работе вам необходимо переопределить методы hashCode() и equals (Object o)
В худшем случае O (n)
- Второй подход - использовать TreeSet
public Object getObjectReference(TreeSet<Xobject> set, Xobject obj) {
if (set.contains(obj)) {
return set.floor(obj);
}
return null;
}
Этот подход дает O (log (n)), более эффективный.
Вам не нужно переопределять hashCode для этого подхода, но вам нужно реализовать интерфейс Comparable. (определите функцию compareTo (Object o))
Ответ 7
Если вы можете использовать List
как структуру данных для хранения ваших данных, вместо использования Map
для сохранения результата в значении карты вы можете использовать следующий фрагмент и сохранить результат в том же объекте.
Вот класс Node:
private class Node {
public int row, col, distance;
public Node(int row, int col, int distance) {
this.row = row;
this.col = col;
this.distance = distance;
}
public boolean equals(Object o) {
return (o instanceof Node &&
row == ((Node) o).row &&
col == ((Node) o).col);
}
}
Если вы сохраняете свой результат в переменной distance, и элементы в списке проверяются на основе их координат, вы можете использовать следующее, чтобы изменить расстояние до нового с помощью lastIndexOf, пока вам нужно хранить только один элемент для каждой информации:
List<Node> nodeList;
nodeList = new ArrayList<>(Arrays.asList(new Node(1, 2, 1), new Node(3, 4, 5)));
Node tempNode = new Node(1, 2, 10);
if(nodeList.contains(tempNode))
nodeList.get(nodeList.lastIndexOf(tempNode)).distance += tempNode.distance;
В основном это переопределение Set
, элементы которого могут быть доступны и изменены.