Получение элемента из набора

324

Почему не Setпредусмотрена операция для получения элемента, равного другому элементу?

Set<Foo> set = ...;
...
Foo foo = new Foo(1, 2, 3);
Foo bar = set.get(foo);   // get the Foo element from the Set that equals foo

Я могу спросить, Setсодержит ли элемент элемент, равный bar, так почему я не могу получить этот элемент? :(

Чтобы уточнить, equalsметод переопределен, но он проверяет только одно из полей, а не все. Таким образом, два Fooобъекта, которые считаются равными, на самом деле могут иметь разные значения, поэтому я не могу просто использовать foo.

Foobar
источник
2
Этот пост уже широко обсуждался, и были предложены хорошие ответы. Однако, если вы просто ищете упорядоченный набор, просто используйте SortedSetи его реализации, которые основаны на карте (например, TreeSetпозволяет получить доступ first()).
Элиран Малка
3
Я тоже пропускаю этот метод для точно такого же случая, который вы описали выше. Objective-C ( NSSet) имеет такой метод. Он вызывается memberи возвращает объект в наборе, который сравнивает «равный» с параметром memberметода (который, конечно, может быть другим объектом и также иметь разные свойства, которые равенство может не проверять).
Меки

Ответы:

118

Не было бы смысла получать элемент, если он равен. А Mapлучше подходит для этого варианта использования.


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

public static void main(String[] args) {

    Set<Foo> set = new HashSet<Foo>();
    set.add(new Foo("Hello"));

    for (Iterator<Foo> it = set.iterator(); it.hasNext(); ) {
        Foo f = it.next();
        if (f.equals(new Foo("Hello")))
            System.out.println("foo found");
    }
}

static class Foo {
    String string;
    Foo(String string) {
        this.string = string;
    }
    @Override
    public int hashCode() { 
        return string.hashCode(); 
    }
    @Override
    public boolean equals(Object obj) {
        return string.equals(((Foo) obj).string);
    }
}
dacwe
источник
234
Там может быть смысл получить элемент. Что если вы хотите обновить некоторые значения элемента после того, как он уже был добавлен в Set? Например, когда .equals () не использует все поля, как указано в OP. Менее эффективное решение было бы удалить элемент и повторно добавить его с обновленными значениями.
KyleM
14
Я бы все еще утверждал, что Mapлучше подходит ( Map<Foo, Foo>в данном случае)
dacwe
22
@dacwe, я попал сюда, потому что начал искать способ избежать именно этого! Объект, который действует одновременно как ключ и соответствующее значение, является именно тем, о чем должен быть весь набор. В моем случае я хотел бы получить какой-то сложный объект из набора по ключу (String). Эта строка инкапсулирована (и уникальна) для отображаемого объекта. Фактически весь объект «вращается» вокруг указанного ключа. Кроме того, вызывающая сторона знает указанную строку, но не сам объект; именно поэтому он хочет получить его по ключу. Я сейчас использую карту, конечно, но это остается странным поведением.
pauluss86
4
@KyleM Я понимаю вариант использования, но хочу подчеркнуть важность не трогать атрибуты, которые являются частью hashCode / equals. Из набора Javadoc: «Примечание. Следует проявлять особую осторожность, если в качестве элементов набора используются изменяемые объекты. Поведение набора не указывается, если значение объекта изменяется таким образом, что это влияет на сравнение с равными, в то время как объект является элемент в наборе. " - Я рекомендую эти объекты быть неизменными или, по крайней мере, иметь неизменные ключевые атрибуты.
stivlo
5
Я согласен, что вы можете использовать Map<Foo, Foo>в качестве замены, недостатком является то, что карта всегда должна хранить, по крайней мере, ключ и значение (и для производительности она также должна хранить хэш), в то время как набор может уйти, просто сохраняя значение (и возможно хэш для производительности). Таким образом, хорошая реализация набора может быть одинаково быстрой, Map<Foo, Foo>но использовать до 50% меньше памяти. В случае Java это не имеет значения, так как HashSet все равно основан на HashMap.
Меки
372

Чтобы ответить на точный вопрос « Почему не Setпредусмотрена операция для получения элемента, равного другому элементу?», Ответ был бы таков: дизайнеры каркаса коллекции были не очень дальновидны. Они не ожидали вашего вполне законного варианта использования, наивно пытались «смоделировать абстракцию математического набора» (из javadoc) и просто забыли добавить полезный get()метод.

Теперь к подразумеваемому вопросу « как вы получаете элемент тогда»: я думаю, что лучшее решение состоит в том, чтобы использовать Map<E,E>вместо Set<E>, чтобы отобразить элементы на себя. Таким образом, вы можете эффективно извлекать элемент из «set», потому что метод get () Mapнайдет элемент, используя эффективную хеш-таблицу или алгоритм дерева. Если вы хотите, вы можете написать свою собственную реализацию, Setкоторая предлагает дополнительный get()метод, инкапсулируя Map.

Следующие ответы, на мой взгляд, плохие или неправильные:

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

«У вас нет другого выбора, кроме как использовать итератор»: это линейный поиск по коллекции, который совершенно неэффективен для больших наборов (по иронии судьбы, внутренне Setон организован как хэш-карта или дерево, которое можно эффективно запрашивать). Не делай этого! Я видел серьезные проблемы с производительностью в реальных системах с использованием этого подхода. На мой взгляд, что ужасного в пропущенном get()методе, так это то, что обходиться с ним немного громоздко, а то, что большинство программистов будут использовать метод линейного поиска, не задумываясь о последствиях.

jschreiner
источник
27
Мех. Проблема в том, чтобы переопределить реализацию equals, чтобы неравные объекты были «равны». Запрашивать метод, который говорит: «Приведите мне объект, идентичный этому объекту», а затем ожидайте, что неидентичный объект будет возвращен, кажется сумасшедшим и легко вызывает проблемы обслуживания. Как и предполагали другие, использование карты решает все эти проблемы: и делает то, что вы делаете, самоочевидным. Легко понять, что два неравных объекта могут иметь один и тот же ключ на карте, а наличие одного и того же ключа покажет связь между ними.
Дэвид Огрен
20
Сильные слова, @ Дэвид Огрен. Мех? Псих? Но в своем комментарии вы используете слова «идентичный» и «равный», как будто они означают одно и то же. Они не. В частности, в Java идентичность выражается оператором "==", а равенство - методом equals (). Если бы они имели в виду одно и то же, не было бы необходимости в методе equals (). На других языках это, конечно, может быть другим. Например, в Groovy identity - это метод is (), а равенство - "==". Забавно, не правда ли?
jschreiner
15
Ваша критика использования мной слова «идентично», когда я должен был использовать слово «эквивалент», очень верна. Но определение равных для объекта так, что Foo и Bar «равны», но не «достаточно равны», чтобы он мог использовать их эквивалентно, создаст все виды проблем как с функциональностью, так и с удобочитаемостью / ремонтопригодностью. Эта проблема с Set только верхушка айсберга для потенциальных проблем. Например, одинаковые объекты должны иметь одинаковые хеш-коды. Так что у него будут потенциальные коллизии хешей. Это безумие - возражать против вызова .get (foo), чтобы получить что-то, кроме foo?
Дэвид Огрен
12
Вероятно, стоит отметить, что, например, HashSet реализован как оболочка вокруг HashMap (которая отображает ключи в фиктивное значение). Таким образом, использование HashMap явно вместо HashSet не приведет к дополнительным расходам в использовании памяти.
Алексей Б.
4
@ user686249 Я чувствую, что это превратилось в академическую дискуссию. Я признаю, что, возможно, я был за бортом, возражая против превосходящих равных. Особенно в использовании, как у вас. Но у меня все еще есть возражение против идеи вызова этого метода get(). В вашем примере я был бы очень смущен customerSet.get (thisCustomer). (Принимая во внимание, что карта, как подсказывают многие ответы) будет просто хорошо с canonicalCustomerMap.get (этот клиент). Я также согласился бы с методом с более четким именем (таким как метод члена Objective C в NSSet).
Дэвид Огрен
19

Если у вас есть равный объект, зачем вам тот из набора? Если он «равен» только по ключу, Mapто лучшим вариантом будет.

Во всяком случае, следующее будет делать это:

Foo getEqual(Foo sample, Set<Foo> all) {
  for (Foo one : all) {
    if (one.equals(sample)) {
      return one;
    }
  } 
  return null;
}

С Java 8 это может стать одним вкладышем:

return all.stream().filter(sample::equals).findAny().orElse(null);
Арне Бурмейстер
источник
Мне больше нравится этот ответ, я бы просто избегал использования двух операторов return, так как это против OOP, и это делает значение Cyclomatic Complexity выше.
Лев
8
@ Спасибо, но парадигма единого выхода не против ООП и в основном недопустима для языков, более современных, чем Фортран или COBOL, см. Также softwareengineering.stackexchange.com/questions/118703/…
Арне Бурмайстер
1
Использование карты вместо набора кажется лучшим вариантом: перебор элементов набора - это больше работы, чем получение одного значения из карты. (O (N) против O (1))
Джейми Флурной
@JamieFlournoy правильно, если вам нужно проверять один и тот же набор на разные элементы несколько раз, это намного лучше. Для однократного использования нет, так как сначала требуется больше усилий для построения карты.
Арне Бурмейстер
18

Преобразовать набор в список, а затем использовать getметод списка

Set<Foo> set = ...;
List<Foo> list = new ArrayList<Foo>(set);
Foo obj = list.get(0);
К кра
источник
37
Я не понимаю этого. Это позволит получить произвольный объект из набора. Не объект.
aioobe
14

Набор по умолчанию в Java, к сожалению, не предназначен для обеспечения операции get, как точно объяснил jschreiner .

Решения использования итератора для поиска интересующего элемента (предложенного dacwe ) или для удаления элемента и его повторного добавления с обновленными значениями (предложенными KyleM ) могут работать, но могут быть очень неэффективными.

Переопределение реализации equals, так что неравные объекты «равны», как правильно сказал Дэвид Огрен , может легко вызвать проблемы с обслуживанием.

И использование Map в качестве явной замены (как предлагают многие), imho, делает код менее элегантным.

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


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

Передача объекта через Интернет означала, что у клиента все равно были разные экземпляры этого объекта. Чтобы сопоставить этот «скопированный» экземпляр с исходным, я решил использовать Java UUID.

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

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

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

По этим причинам я реализовал библиотеку под названием MagicSet, которая делает использование Map «прозрачным» для разработчика.

https://github.com/ricpacca/magicset


Как и исходный Java HashSet, MagicHashSet (который является одной из реализаций MagicSet, представленных в библиотеке) использует вспомогательный HashMap, но вместо того, чтобы использовать элементы в качестве ключей и фиктивное значение в качестве значений, он использует UUID элемента в качестве ключа и сам элемент как ценность. Это не вызывает накладных расходов при использовании памяти по сравнению с обычным HashSet.

Более того, MagicSet может использоваться именно как Set, но с некоторыми другими методами, обеспечивающими дополнительные функциональные возможности, такие как getFromId (), popFromId (), removeFromId () и т. Д.

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


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

class City extends UniqueItem {

    // Somewhere in this class

    public void doSomething() {
        // Whatever
    }
}

public class GameMap {
    private MagicSet<City> cities;

    public GameMap(Collection<City> cities) {
        cities = new MagicHashSet<>(cities);
    }

    /*
     * cityId is the UUID of the city you want to retrieve.
     * If you have a copied instance of that city, you can simply 
     * call copiedCity.getId() and pass the return value to this method.
     */
    public void doSomethingInCity(UUID cityId) {
        City city = cities.getFromId(cityId);
        city.doSomething();
    }

    // Other methods can be called on a MagicSet too
}
ricpacca
источник
11

Если ваш набор на самом деле NavigableSet<Foo>(например, TreeSet), и Foo implements Comparable<Foo>вы можете использовать

Foo bar = set.floor(foo); // or .ceiling
if (foo.equals(bar)) {
    // use bar…
}

(Спасибо за комментарий @ eliran-malka за подсказку.)

Джесси Глик
источник
5
Если бы я не возражал против того, чтобы кто-то читал мой код, сначала подумав, что я сошел с ума, это было бы отличным решением.
Адам
10

С Java 8 вы можете сделать:

Foo foo = set.stream().filter(item->item.equals(theItemYouAreLookingFor)).findFirst().get();

Но будьте осторожны, .get () генерирует исключение NoSuchElementException, или вы можете манипулировать необязательным элементом.

облачная погода
источник
5
item->item.equals(theItemYouAreLookingFor)можно сократить доtheItemYouAreLookingFor::equals
Хенно Вермёлен
5
Object objectToGet = ...
Map<Object, Object> map = new HashMap<Object, Object>(set.size());
for (Object o : set) {
    map.put(o, o);
}
Object objectFromSet = map.get(objectToGet);

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

Xymon
источник
5

Зачем:

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

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

Из JavaDocs

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

Как:

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

mySet.stream()
.filter(object -> object.property.equals(myProperty))
.findFirst().get();
Джейсон М
источник
2

Как насчет использования класса Arrays?

import java.util.Arrays;
import java.util.List;
import java.util.HashSet;
import java.util.Arrays;

public class MyClass {
    public static void main(String args[]) {
        Set mySet = new HashSet();
        mySet.add("one");
        mySet.add("two");
        List list = Arrays.asList(mySet.toArray());
        Object o0 = list.get(0);
        Object o1 = list.get(1);
        System.out.println("items " + o0+","+o1);
    }
}

вывод:
элементы один, два

скорость
источник
1

Я знаю, об этом уже давно спрашивали и отвечали, однако, если кому-то интересно, вот мое решение - пользовательский класс, поддерживаемый HashMap:

http://pastebin.com/Qv6S91n9

Вы можете легко реализовать все другие методы Set.

Лукас З.
источник
7
Желательно включать пример, а не просто ссылаться на него.
Все работники необходимы
1

Там было сделано это! Если вы используете Guava, быстрый способ преобразовать его в карту:

Map<Integer,Foo> map = Maps.uniqueIndex(fooSet, Foo::getKey);
Гейзенберг
источник
1

Вы можете использовать класс Iterator

import java.util.Iterator;
import java.util.HashSet;

public class MyClass {
 public static void main(String[ ] args) {
 HashSet<String> animals = new HashSet<String>();
animals.add("fox");
animals.add("cat");
animals.add("dog");
animals.add("rabbit");

Iterator<String> it = animals.iterator();
while(it.hasNext()) {
  String value = it.next();
  System.out.println(value);   
 }
 }
}
чиха асма
источник
1

Если вам нужен n-й элемент из HashSet, вы можете воспользоваться приведенным ниже решением, здесь я добавил объект ModelClass в HashSet.

ModelClass m1 = null;
int nth=scanner.nextInt();
for(int index=0;index<hashset1.size();index++){
    m1 = (ModelClass) itr.next();
    if(nth == index) {
        System.out.println(m1);
        break;
    }
}
Хардик Патель
источник
1

Если вы посмотрите на первые несколько строк реализации, java.util.HashSetвы увидите:

public class HashSet<E>
    ....
    private transient HashMap<E,Object> map;

Таким образом, в любом случае HashSetиспользуется HashMapвнутренне, что означает, что если вы просто используете a HashMapи используете то же значение, что и ключ, и значение, вы получите желаемый эффект и сэкономите себе немного памяти.

rghome
источник
1

похоже, что правильным объектом для использования является Interner из guava:

Обеспечивает эквивалентное поведение для String.intern () для других неизменяемых типов. Общие реализации доступны из класса Interners .

У него также есть несколько очень интересных рычагов, таких как concurrencyLevel или тип используемых ссылок (возможно, стоит отметить, что он не предлагает SoftInterner, который я считаю более полезным, чем WeakInterner).

molyss
источник
0

Потому что любая конкретная реализация Set может иметь или не иметь произвольный доступ .

Вы всегда можете получить итератор и пройти через Set, используя метод итераторов, next()чтобы вернуть желаемый результат, когда вы найдете равный элемент. Это работает независимо от реализации. Если реализация НЕ является произвольным доступом (представьте набор на основе связанного списка), get(E element)метод в интерфейсе был бы обманчивым, так как он должен был бы выполнить итерацию коллекции, чтобы найти возвращаемый элемент, и a get(E element), по-видимому, подразумевает, что это будет Необходимо, чтобы Набор мог перейти непосредственно к элементу, чтобы получить.

contains() может или не может делать то же самое, конечно, в зависимости от реализации, но название, похоже, не поддается тому же виду недоразумений.

Том Тресанский
источник
2
Все, что сделал бы метод get (), уже выполняется методом contains (). Вы не можете реализовать contains (), не получив содержащийся объект и не вызвав его метод .equals (). Разработчики API, похоже, не стеснялись добавлять get () в java.util.List, хотя в некоторых реализациях это было бы медленно.
Брайан Ринк
Я не думаю, что это правда. Два объекта могут быть равны через равенства, но не идентичны через ==. Если у вас есть объект A и набор S, содержащий объект B и A.equals (B), но A! = B, и вы хотите получить ссылку на B, вы можете вызвать S.get (A), чтобы получить ссылку на B, предполагая, что у вас есть метод get с семантикой метода get List, который отличается от случая использования проверки, если S.contains (A) (что было бы). Это даже не редкий случай использования коллекций.
Том Тресанский
0

Да, использовать HashMap... но специализированным способом: ловушка, которую я предвижу при попытке использовать a HashMapв качестве псевдо, Set- это возможная путаница между "фактическими" элементами Map/Setи "потенциальными" элементами, то есть элементами, используемыми для проверки, является ли equalэлемент уже присутствует. Это далеко не надежно, но выталкивает вас из ловушки:

class SelfMappingHashMap<V> extends HashMap<V, V>{
    @Override
    public String toString(){
        // otherwise you get lots of "... object1=object1, object2=object2..." stuff
        return keySet().toString();
    }

    @Override
    public V get( Object key ){
        throw new UnsupportedOperationException( "use tryToGetRealFromCandidate()");
    }

    @Override
    public V put( V key, V value ){
       // thorny issue here: if you were indavertently to `put`
       // a "candidate instance" with the element already in the `Map/Set`: 
       // these will obviously be considered equivalent 
       assert key.equals( value );
       return super.put( key, value );
    }

    public V tryToGetRealFromCandidate( V key ){
        return super.get(key);
    }
}

Затем сделайте это:

SelfMappingHashMap<SomeClass> selfMap = new SelfMappingHashMap<SomeClass>();
...
SomeClass candidate = new SomeClass();
if( selfMap.contains( candidate ) ){
    SomeClass realThing = selfMap.tryToGetRealFromCandidate( candidate );
    ...
    realThing.useInSomeWay()...
}

Но ... теперь вы хотите candidateкаким-то образом самоуничтожиться, если программист на самом деле не поместит его в Map/Set... вы хотите contains"испортить" его, candidateчтобы любое его использование, если оно не присоединялось, Mapпревращало его в "анафему" ». Возможно, вы могли бы SomeClassреализовать новый Taintableинтерфейс.

Более удовлетворительное решение - GettableSet , как показано ниже. Однако, чтобы это работало, вы должны либо отвечать за разработку SomeClass, чтобы сделать все конструкторы невидимыми (или ... иметь возможность и желать разрабатывать и использовать для этого класс-оболочку):

public interface NoVisibleConstructor {
    // again, this is a "nudge" technique, in the sense that there is no known method of 
    // making an interface enforce "no visible constructor" in its implementing classes 
    // - of course when Java finally implements full multiple inheritance some reflection 
    // technique might be used...
    NoVisibleConstructor addOrGetExisting( GettableSet<? extends NoVisibleConstructor> gettableSet );
};

public interface GettableSet<V extends NoVisibleConstructor> extends Set<V> {
    V getGenuineFromImpostor( V impostor ); // see below for naming
}

Реализация:

public class GettableHashSet<V extends NoVisibleConstructor> implements GettableSet<V> {
    private Map<V, V> map = new HashMap<V, V>();

    @Override
    public V getGenuineFromImpostor(V impostor ) {
        return map.get( impostor );
    }

    @Override
    public int size() {
        return map.size();
    }

    @Override
    public boolean contains(Object o) {
        return map.containsKey( o );
    }

    @Override
    public boolean add(V e) {
        assert e != null;
        V result = map.put( e,  e );
        return result != null;
    }

    @Override
    public boolean remove(Object o) {
        V result = map.remove( o );
        return result != null;
    }

    @Override
    public boolean addAll(Collection<? extends V> c) {
        // for example:
        throw new UnsupportedOperationException();
    }

    @Override
    public void clear() {
        map.clear();
    }

    // implement the other methods from Set ...
}

Ваши NoVisibleConstructorзанятия выглядят так:

class SomeClass implements NoVisibleConstructor {

    private SomeClass( Object param1, Object param2 ){
        // ...
    }

    static SomeClass getOrCreate( GettableSet<SomeClass> gettableSet, Object param1, Object param2 ) {
        SomeClass candidate = new SomeClass( param1, param2 );
        if (gettableSet.contains(candidate)) {
            // obviously this then means that the candidate "fails" (or is revealed
            // to be an "impostor" if you will).  Return the existing element:
            return gettableSet.getGenuineFromImpostor(candidate);
        }
        gettableSet.add( candidate );
        return candidate;
    }

    @Override
    public NoVisibleConstructor addOrGetExisting( GettableSet<? extends NoVisibleConstructor> gettableSet ){
       // more elegant implementation-hiding: see below
    }
}

PS Одна техническая проблема с таким NoVisibleConstructorклассом: можно возразить, что такой класс неотъемлемо final, что может быть нежелательно. На самом деле вы всегда можете добавить фиктивный protectedконструктор без параметров :

protected SomeClass(){
    throw new UnsupportedOperationException();
}

... который по крайней мере позволит компиляции подкласса. Затем вам нужно подумать о том, нужно ли вам включать другой getOrCreate()фабричный метод в подкласс.

Последний шаг - это абстрактный базовый класс (NB «элемент» для списка, «член» для набора), подобный этому для членов вашего набора (когда это возможно - опять же, область применения класса-оболочки, где этот класс не находится под вашим контролем, или уже имеет базовый класс и т. д.) для максимального сокрытия реализации:

public abstract class AbstractSetMember implements NoVisibleConstructor {
    @Override
    public NoVisibleConstructor
            addOrGetExisting(GettableSet<? extends NoVisibleConstructor> gettableSet) {
        AbstractSetMember member = this;
        @SuppressWarnings("unchecked") // unavoidable!
        GettableSet<AbstractSetMembers> set = (GettableSet<AbstractSetMember>) gettableSet;
        if (gettableSet.contains( member )) {
            member = set.getGenuineFromImpostor( member );
            cleanUpAfterFindingGenuine( set );
        } else {
            addNewToSet( set );
        }
        return member;
    }

    abstract public void addNewToSet(GettableSet<? extends AbstractSetMember> gettableSet );
    abstract public void cleanUpAfterFindingGenuine(GettableSet<? extends AbstractSetMember> gettableSet );
}

... использование достаточно очевидно (внутри SomeClass«s staticфабричного метода):

SomeClass setMember = new SomeClass( param1, param2 ).addOrGetExisting( set );
Майк Грызун
источник
0

Контракт хеш-кода дает понять, что:

«Если два объекта равны в соответствии с методом Object, то вызов метода hashCode для каждого из двух объектов должен привести к одинаковому целочисленному результату».

Итак, ваше предположение:

«Чтобы уточнить, метод equals переопределен, но он проверяет только одно из полей, а не все. Поэтому два объекта Foo, которые считаются равными, могут фактически иметь разные значения, поэтому я не могу просто использовать foo».

неправильно, и вы нарушаете контракт. Если мы посмотрим на «содержащий» метод интерфейса Set, мы увидим, что:

логическое значение содержит (объект o);
Возвращает true, если этот набор содержит указанный элемент. Более формально, возвращает true тогда и только тогда, когда этот набор содержит элемент "e" такой, что o == null? e == null: o.equals (e)

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

jfajunior
источник
-2

Быстрый вспомогательный метод, который может решить эту ситуацию:

<T> T onlyItem(Collection<T> items) {
    if (items.size() != 1)
        throw new IllegalArgumentException("Collection must have single item; instead it has " + items.size());

    return items.iterator().next();
}
Крис Уиллмор
источник
8
Очень странно, что этот ответ получил так много голосов, поскольку он не отвечает на вопрос и даже не пытается ответить на него каким-либо образом.
Дэвид Конрад
-2

После может быть подход

   SharedPreferences se_get = getSharedPreferences("points",MODE_PRIVATE);
   Set<String> main = se_get.getStringSet("mydata",null);
   for(int jk = 0 ; jk < main.size();jk++)
   {
      Log.i("data",String.valueOf(main.toArray()[jk]));
   }
Vikrant
источник
-2

Попробуйте использовать массив:

ObjectClass[] arrayName = SetOfObjects.toArray(new ObjectClass[setOfObjects.size()]);
canni
источник