Подтвердить что ты не робот

Почему интерфейс java.util.Set <V> не предоставляет метод get (Object o)?

Я понимаю, что только один экземпляр любого объекта в соответствии с .equals() разрешен в наборе и что вам не нужно "нужно" получить объект из набора, если у вас уже есть эквивалентный объект, но я бы по-прежнему нравится иметь метод .get(), который возвращает фактический экземпляр объекта в Set (или null), заданный эквивалентным объектом в качестве параметра.

Любые идеи/теории о том, почему он был создан таким образом?

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

EDIT: Я не думаю, что люди до сих пор понимают мой вопрос. Я хочу, чтобы конкретный экземпляр объекта уже был в наборе, а не, возможно, другой экземпляр объекта, где .equals() возвращает true.

Что касается того, почему я хотел бы этого поведения, обычно .equals() не учитывает все свойства объекта. Я хочу предоставить некоторый фиктивный объект поиска и вернуть экземпляр фактического объекта в Set.

4b9b3361

Ответ 1

В то время как аргумент чистоты делает метод get(Object) подозрительным, основное намерение не является спорным.

Существуют различные семейства классов и интерфейсов, которые немного переопределяют equals(Object). Нужно смотреть не дальше, чем интерфейсы коллекций. Например, ArrayList и LinkedList могут быть равны; их соответствующее содержание просто должно быть одинаковым и в том же порядке.

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

public interface Collection<E> extends ... {
  ...
  public E findMatch(Object o) throws UnsupportedOperationException;
  ...
}

Обратите внимание, что этот API имеет более широкое значение, чем в Set.

Что касается самого вопроса, у меня нет теории о том, почему такая операция была опущена. Я скажу, что минимальный аргумент spanning set не выполняется, поскольку многие операции, определенные в API-интерфейсах коллекций, мотивированы удобством и эффективностью.

Ответ 2

Проблема в том, что Set не для "получения" объектов, это для добавления и проверки наличия. Я понимаю, что вы ищете, у меня была аналогичная ситуация, и я закончил использование карты того же объекта в ключе и значении.

EDIT: просто уточнить: http://en.wikipedia.org/wiki/Set_(abstract_data_type)

Ответ 3

У меня был тот же вопрос в java-форуме много лет назад. Они сказали мне, что интерфейс Set определен. Его нельзя изменить, поскольку он нарушит текущие реализации интерфейса Set. Затем они начали требовать фуфло, как вы видите здесь: "Set не нужен метод get" и начал бурить меня, что Map всегда должен использоваться для получения элементов из набора.

Если вы используете набор только для математических операций, таких как пересечение или объединение, то может быть contains() достаточно. Однако Set хранится в коллекциях для хранения данных. Я объяснил, что нужно get() в Set, используя реляционную модель данных.

В дальнейшем таблица SQL похожа на класс. Столбцы определяют атрибуты (известные как поля в Java), а записи представляют экземпляры класса. Так что объект является вектором полей. Некоторые из полей являются первичными ключами. Они определяют уникальность объекта. Это то, что вы делаете для contains() в Java:

class Element {

        public int hashCode() {return sumOfKeyFields()}
        public boolean equals(Object e) {keyField1.equals(e) && keyField2.equals(e) && ..}

Я не знаю внутренних компонентов БД. Но вы определяете ключевые поля только один раз, когда задаете таблицу. Вы просто комментируете ключевые поля с помощью @primary. Вы не указываете ключи второй раз, когда добавляете запись в таблицу. Вы не отделяете ключи от данных, как и при сопоставлении. Таблицы SQL - это множества. Они не карты. Тем не менее, они обеспечивают get() в дополнение к сохранению уникальности и содержат() проверку.

В "Искусстве компьютерного программирования", вводя поиск, Д. Кнут говорит то же самое:

Большая часть этой главы посвящена изучению очень простого поиска проблема: как найти данные, которые были сохранены с данным идентификация.

Видите ли, данные хранятся с идентификацией. Не идентификация, указывающая на данные, но данные с идентификацией. Он продолжает:

Например, в числовом приложении мы можем захотеть найти f (x), заданную x и таблицу значений f; в нетривиальное приложение, мы можем захотеть найти английский перевод данного русского слова.

Похоже, он начинает говорить о картографии. Однако

В общем случае мы будем предполагать, что набор из N записей сохранен, и проблема состоит в том, чтобы найти подходящую. Обычно мы требуем N ключей должны быть разными, так что каждый ключ однозначно идентифицирует его запись. Сбор всех записей называется таблицей или файлом, где слово "таблица" обычно используется для указания небольшого файла и "Файл" обычно используется для указания большой таблицы. Большой файл или группа файлов часто называется базой данных.

Алгоритмы поиска представлены с так называемым аргументом K, и проблема заключается в том, чтобы найти, какая запись имеет K в качестве ключа. Хотя Целью поиска является поиск информации, хранящейся в записи связанных с K, алгоритмы в этой главе обычно игнорируют все, кроме самих ключей. На практике мы можем найти связанные данные, как только мы находим K; например, если K появляется в location ТАБЛИЦА + i, связанные данные (или указатель на него) могут быть в месте ТАБЛИЦА + я + 1

Таким образом, поиск находит ключ, зарегистрированный в записи, и он не должен "отображать" ключ к данным. Оба они расположены в одной записи, например, в файлах java-объекта. То есть алгоритм поиска исследует ключевые поля записи, как в наборе, а не какой-то удаленный ключ, как это делается на карте.

Нам присваивается N элементов для сортировки; мы будем называть их записями и вся коллекция N записей будет называться файлом. каждый запись Rj имеет ключ Kj, который определяет процесс сортировки. дополнительный данные, помимо ключа, обычно также присутствуют; этот дополнительный "спутник информация" не влияет на сортировку, за исключением того, что она должна быть перенесена как часть каждой записи.

Я также не вижу необходимости дублировать ключи в дополнительном "наборе ключей" при обсуждении сортировки.

... [ "Искусство компьютерного программирования", глава 6, Введение]

набор объектов представляет собой набор или задает все сущности определенного типа объекта [Http://wiki.answers.com/Q/What_is_entity_and_entity_set_in_dbms] Объекты одного класса делят свои атрибуты класса. Точно так же делайте записи в БД. Они разделяют атрибуты столбцов.

Частным случаем коллекции является степень класса, которая является сбор всех объектов, принадлежащих классу. Класс экстентов позволяет классы, которые должны рассматриваться как отношения

... [ "Концепции системы баз данных", 6-е издание]

В основном класс описывает атрибуты, общие для всех его экземпляров. Таблица в реляционной БД делает то же самое. "Самое легкое сопоставление, которое у вас когда-либо будет, - это сопоставление свойств одного атрибута с одним столбцом" . Это тот случай, о котором я говорю.

Я так многократно доказываю аналогию (изоморфизм) между объектами и записями БД, потому что есть глупые люди, которые этого не принимают (чтобы доказать, что их Набор не должен иметь метод get)

Вы видите в повторах, как люди, которые не понимают этого, говорят, что Set with get будет лишним? Это связано с тем, что их злоупотребляемая карта, которую они налагают на использование вместо множества, вводит избыточность. Их вызов put (obj.getKey(), obj) хранит два ключа: исходный ключ как часть объекта и его копию в наборе ключей карты. Дублирование - это избыточность. Он также включает в себя больше раздувания в коде и потери памяти, потребляемой в Runtime. Я не знаю о внутренностях БД, но принципы хорошего дизайна и нормализации базы данных говорят, что такое дублирование - плохая идея - должен быть только один источник истины. Избыточность означает, что может возникнуть несогласованность: ключ сопоставляется с объектом, у которого есть другой ключ. Несоответствие - это проявление избыточности. Эдгар Ф. Кодд предложил нормализацию БД, чтобы избавиться от увольнений и их предполагаемых несоответствий. Учителя четко указывают на нормализацию: Нормализация никогда не создаст две таблицы с взаимно-однозначным отношением между ними. Нет никакой теоретической причины для разделения одного объекта, подобного этому, с некоторыми полями в одной записи одной таблицы, а другие - в одной записи другой таблицы

Итак, у нас есть 4 аргумента, почему использование карты для реализации get in set плохо:

  • карта не нужна, если у нас есть набор уникальных объектов
  • отображает избыточность в хранилище времени выполнения
  • карта вводит разбухание кода в БД (в Коллекциях)
  • с использованием карты противоречит нормализации хранения данных

Даже если вы не знаете о идее записи и нормализации данных, играя с коллекциями, вы можете обнаружить эту структуру данных и алгоритм самостоятельно, как это сделали мы, разработчики org.eclipse.KeyedHashSet и С++ STL.

Мне запретили участвовать в форуме Sun, чтобы указать на эти идеи. Фанатизм - единственный аргумент против причины, и в этом мире доминируют фанатики. Они не хотят видеть концепции и то, как вещи могут быть разными/улучшены. Они видят только реальный мир и не могут себе представить, что дизайн коллекций Java может иметь недостатки и может быть улучшен. Этим людям опасно напоминать логические вещи. Они учат вас своей слепоте и наказывают, если вы не слушаетесь.

Добавлено декабрь 2013: SICP также говорит, что DB - это набор с записями с ключами, а не с картой:

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

Ответ 4

Я не уверен, что вы ищете объяснение того, почему Sets ведут себя таким образом, или для простого решения проблемы, которую он представляет. Другие ответы касались первого, поэтому здесь предложение для последнего.

Вы можете выполнить итерацию по элементам Set и проверить каждый из них для равенства с помощью метода equals(). Это легко реализовать и вряд ли подвержено ошибкам. Очевидно, если вы не уверены в том, что элемент находится в наборе или нет, сначала проверьте с помощью метода contains().

Это неэффективно по сравнению с, например, методом HashSet contains(), который "находит" сохраненный элемент, но не возвращает его. Если ваши наборы могут содержать много элементов, это может быть даже причиной использования "более тяжелого" обходного пути, такого как реализация карты, о которой вы говорили. Однако, если это важно для вас (и я вижу преимущество использования этой способности), это, вероятно, стоит того.

Ответ 5

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

Например,

Integer a = new Integer(3);
Integer b = new Integer(3);

В этом случае a.equals(b), потому что они относятся к одному и тому же внутреннему значению, но a!= b, потому что это два разных объекта.

Существуют другие реализации Set, такие как IdentitySet, которые различают сравнение между элементами.

Однако, я думаю, вы пытаетесь применить к Java другую философию. Если ваши объекты равны (a.equals(b)), хотя a и b имеют другое состояние или значение, здесь что-то не так. Вы можете разделить этот класс на два или более семантических класса, которые реализуют общий интерфейс - или, возможно, пересмотреть .equals и .hashCode.

Если у вас есть Joshua Bloch Эффективная Java, посмотрите главы, которые называются "Выполняйте общий контракт при переопределении равных" и "Свернуть изменчивость".

Ответ 6

Просто используйте решение Map... TreeSet и HashSet также делают это, так как они подкреплены TreeMap и HashMap, поэтому нет никакого штрафа при этом (в действительности это должен быть минимальный выигрыш).

Вы также можете расширить свой любимый набор, чтобы добавить метод get().

[]]

Ответ 7

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

K target = ...;
Set<K> set = ...;
for (K element : set) {
  if (target.equals(element)) {
    return element;
  }
}

Ответ 8

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

public <T> T findInSet(T findMe, Set<T> inHere){
   inHere.retainAll(Arrays.asList(findMe));
   if(!inHere.isEmpty){
       return inHere.iterator().next();
   }
   return null;
}

Это не самое эффективное использование памяти, но функционально и математически корректно.

Ответ 9

Хорошо, если вы уже "получили" вещь из набора, вам не нужно ее получать(), не так ли?; -)

Ваш подход к использованию карты - это правильная вещь, я думаю. Похоже, вы пытаетесь "канонизировать" объекты с помощью метода equals(), который я всегда выполнял с помощью карты, как вы предлагаете.

Ответ 10

"Я хочу, чтобы экземпляр точного объекта уже был в наборе, а не, возможно, другой экземпляр объекта, где .equals() возвращает true."

Это не имеет смысла. Скажем, вы:

Set<Foo> s = new Set<Foo>();
s.Add(new Foo(...));
...
Foo newFoo = ...;

Теперь вы выполните:

s.contains(newFoo)

Если вы хотите, чтобы это было только true, если объект в наборе == newFoo, реализуйте значения Foo и hashCode с идентификатором объекта. Или, если вы пытаетесь сопоставить несколько одинаковых объектов с каноническим оригиналом, тогда карта может быть правильным выбором.

Ответ 11

Я думаю, что ожидание состоит в том, что equely действительно представляет собой некоторое равенство, а не просто, что два объекта имеют один и тот же первичный ключ, например. И если равные представлены двумя действительно равными объектами, то get будет избыточным. Предполагаемый вариант использования предлагает карту и, возможно, другое значение для ключа, то, что представляет собой первичный ключ, а не весь объект, а затем надлежащим образом реализует равные и хэш-коды соответственно.

Ответ 12

Функциональная Java имеет реализацию постоянного набора (поддерживаемого красным/черным деревом), который, кстати, включает split, который, похоже, делает то, что вы хотите. Он возвращает триплет:

  • Набор всех элементов, которые появляются перед найденным объектом.
  • Объект типа Option, который либо пуст, либо содержит найденный объект, если он существует в наборе.
  • Набор всех элементов, которые появляются после найденного объекта.

Вы сделали бы что-то вроде этого:

MyElementType found = hayStack.split(needle)._2().orSome(hay);

Ответ 13

Object fromSet = set.tailSet(obj).first();

if (! obj.equals(fromSet)) fromSet = null;

делает то, что вы ищете. Я не знаю, почему Java скрывает его.

Ответ 14

Скажем, у меня есть POJO пользователя с идентификатором и именем. ID сохраняет контракт между равными и hashcode. имя не является частью равенства объектов. Я хочу обновить имя пользователя на основе ввода от где-то сказать, UI.

Поскольку набор java не предоставляет метод get, мне нужно выполнить итерацию по набору в моем коде и обновить имя, когда я нахожу равный объект (т.е. когда ID совпадает).

Если у вас есть метод, этот код можно было бы сократить.

Java теперь поставляется со всеми такими глупыми вещами, как javadb и улучшенными для цикла, я не понимаю, почему в этом конкретном случае они являются пуристами.

Ответ 15

У меня была та же проблема. Я исправил это, преобразовывая свой набор в карту, а затем получая их с карты. Я использовал этот метод:

public Map<MyObject, MyObject> convertSetToMap(Set<MyObject> set)
{
    Map<MyObject, MyObject> myObjectMap = new HashMap<MyObject, MyObject>();

    for(MyObject myObject: set){
        myObjectMap.put(myObject, myObject);
    }
    return myObjectMap
}

Теперь вы можете получить элементы из своего набора, вызвав этот метод следующим образом:

convertSetToMap(myset).get(myobject);

Вы можете переопределить равные в своем классе, чтобы он мог проверять только определенные свойства, такие как идентификатор или имя.

Ответ 16

если вы сделали запрос на это в списке параграфов Java, здесь, и мы можем проголосовать за него. Я думаю, по крайней мере, класс удобства java.util.Collections, который просто берет набор и объект и реализовано что-то вроде

searchSet(Set ss, Object searchFor){

        Iterator it = ss.iterator();
        while(it.hasNext()){
            Object s = it.next();
            if(s != null && s.equals(searchFor)){
                return s;
            }
        }

Ответ 17

Это, очевидно, является недостатком Set API.

Просто я хочу найти объект в своем наборе и обновить его свойство.

И мне нужно пройти через мой (хэш) набор, чтобы добраться до моего объекта... Вздох...

Ответ 18

Я согласен с тем, что хотел бы видеть, что реализации Set предоставляют метод get().

Как один из вариантов, в случае, когда ваши объекты реализуют (или могут реализовать) java.lang.Comparable, вы можете использовать TreeSet. Затем функцию типа get() можно получить, вызвав потолок() или floor(), а затем проверит, что результат не равен null и равен объекту сравнения, например:

TreeSet myTreeSet<MyObject> = new TreeSet();
:
:

// Equivalent of a get() and a null-check, except for the incorrect value sitting in
// returnedMyObject in the not-equal case.
MyObject returnedMyObject = myTreeSet.ceiling(comparisonMyObject);

if ((null != returnedMyObject) && returnedMyObject.equals(comparisonMyObject)) {
:
:
}

Ответ 19

Причина, почему нет, проста:

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

Если у вас нет объекта, вам нужно какое-то средство (ключ), чтобы найти его... имя, число, что когда-либо. То, что карты для правильного.

map.get( "ключ" ) → X!

У наборов нет ключей, вам нужно пройти их, чтобы получить объекты.

Итак, почему бы не добавить удобный get (X) → X

Это не имеет никакого смысла, потому что у вас уже есть X, говорит пурист.

Но теперь посмотрите на него как на не пуриста и посмотрите, действительно ли вы этого хотите:

Скажем, я делаю объект Y, совпадающий с равными X, так что set.get(Y) → X. Воля, тогда я могу получить доступ к данным X, которые у меня не было. Скажем, например, у X есть метод под названием get flag(), и я хочу получить результат.

Теперь посмотрите на этот код.

У

X = map.get(Y);

Итак, Y.equals(x) true!

а..

Y.flag() == X.flag() = false. (Не были ли они равны?)

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

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

Ответ 20

Я понимаю, что только один экземпляр любого объекта в соответствии с .equals() разрешен в наборе и что вам не нужно "нужно" получить объект из Set, если у вас уже есть эквивалентный объект, но я бы по-прежнему нравится иметь метод .get(), который возвращает фактический экземпляр объекта в Set (или null), если в качестве параметра используется эквивалентный объект.

Ответ 21

Простой интерфейс /API дает больше свободы во время реализации. Например, если бы интерфейс Set был уменьшен только до одного метода contains(), мы получим определение набора, типичное для функционального программирования, - это просто предикат, на самом деле объекты не хранятся. Это также верно для java.util.EnumSet - он содержит только растровое изображение для каждого возможного значения.

Ответ 22

Это просто мнение. Я считаю, что нам нужно понять, что у нас есть несколько классов java без полей/свойств, т.е. Только методы. В этом случае равные не могут быть измерены путем сравнения функции, одним из таких примеров является requestHandlers. См. Приведенный ниже пример приложения JAX-RS. В этом контексте SET имеет больше смысла, чем любая структура данных.

@ApplicationPath("/")
public class GlobalEventCollectorApplication extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        classes.add(EventReceiverService.class);
        classes.add(VirtualNetworkEventSerializer.class);
        return classes;
    }
}

Чтобы ответить на ваш вопрос, если у вас есть объект мелкого сотрудника (т.е. только EMPID, который используется в методе equals для определения уникальности), и если вы хотите получить глубокий объект, выполнив поиск в наборе, SET не является структурой данных, поскольку ее назначение различно.

Ответ 23

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

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);

list.get(0); // will return value 1

Помните это как простой массив.

Набор - это не упорядоченная структура данных. Поэтому он не следует никакому порядку. Данные, которые вы вставляете в определенную позицию, будут доступны в любой позиции.

Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
//assume it has get method
set.get(0); // what are you expecting this to return. 1?.. 

Но он вернет что-то еще. Следовательно, нет смысла создавать метод get в Set.

** Примечание **** Для объяснения я использовал тип int, это же применимо и для типа объекта.

Ответ 24

Я думаю, вы ответили на свой вопрос: он лишний.

Set предоставляет Set # contains (Object o), который предоставляет эквивалентный тест идентификации вашего нужного Set # get (Object o) и возвращает логическое значение, как и ожидалось.