Отримання елемента з набору


323

Чому не 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.


2
Цей пост вже широко обговорюється, і були запропоновані хороші відповіді. Однак якщо ви просто шукаєте впорядкований набір, просто використовуйте SortedSetта його реалізації, які базуються на карті (наприклад, TreeSetдозволяє отримати доступ first()).
Еліран Малька

3
Я пропускаю цей метод теж для того ж випадку, який ви описали вище. Objective-C ( NSSet) має такий метод. Він викликається, memberі він повертає об'єкт у межах набору, який порівнює "рівний" з параметром memberметоду (який, звичайно, може бути різним об'єктом, а також має різні властивості, що рівне може не перевірити).
Мецькі

Відповіді:


118

Не було б сенсу отримувати елемент, якщо він рівний. A 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);
    }
}

234
Там абсолютно може бути сенс отримати елемент. Що робити, якщо ви хочете оновити деякі значення елемента після того, як вони вже додані до набору? Наприклад, коли .equals () використовує не всі поля, як вказано в ОП. Менш ефективним рішенням буде видалення елемента та повторне додавання його до оновлених значень.
KyleM

14
Я б все-таки стверджував, що a Mapкраще підходить ( Map<Foo, Foo>у цьому випадку)
dacwe

22
@dacwe, я потрапив сюди, бо почав шукати спосіб уникнути саме цього! Об'єкт, який одночасно виступає і ключовим, і відповідним значенням, - це саме те, про що має бути набір. У моєму випадку я хотів би отримати якийсь складний об'єкт із набору за ключем (String). Ця струна інкапсульована (і унікальна) для об’єкта, який відображається. Насправді весь об’єкт «обертається» навколо зазначеного ключа. Крім того, абонент знає сказану 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>для відображення елементів для себе. Таким чином, ви можете ефективно отримати елемент з "набору", тому що метод get () Mapзнайде елемент за допомогою ефективної хеш-таблиці або алгоритму дерева. Якщо ви хочете, ви можете написати власну реалізацію, Setяка пропонує додатковий get()метод, інкапсулюючи Map.

Наступні відповіді, на мою думку, погані чи неправильні:

"Вам не потрібно отримувати елемент, тому що ви вже маєте рівний об'єкт": твердження неправильне, як ви вже показали у запитанні. Два рівні об'єкти все одно можуть мати різний стан, що не має відношення до рівності об'єкта. Мета - отримати доступ до цього стану елемента, що міститься вSet , а не в стані об'єкта, який використовується як "запит".

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


27
мех. Тут переважає реалізація рівностей, щоб нерівні об'єкти були "рівними". Попросити метод, який говорить "дістань мені ідентичний об'єкт цьому об'єкту", а потім очікувати повернення неідентичного об'єкта здається божевільним і легким викликати проблеми з технічним обслуговуванням. Як запропонували інші, використання карти вирішує всі ці проблеми: і це робить те, що ви робите, самозрозумілим. Неважко зрозуміти, що два нерівні об'єкти можуть мати один і той же ключ на карті, а наявність одного ключа показала б взаємозв'язок між ними.
Девід Огрен

20
Сильні слова, @David Ogren. Мех? Божевільний? Але у своєму коментарі ви вживаєте слова "однаковий" та "рівний" так, ніби вони означали те саме. Вони не. Зокрема, у Java ідентичність виражається оператором "==", а рівність виражається методом equals (). Якби вони мали на увазі те саме, не було б потреби в методі equals (). В інших мовах це, звичайно, може бути різним. Наприклад, у Groovy тотожність є методом 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);

Мені подобається ця відповідь краще, я б просто уникав використання двох зворотних заяв, оскільки це проти OOP, і це робить значення Cyclomatic Complexity вище.
Лев

8
@Leo спасибі, але парадигма одного виходу не проти OOP і здебільшого недійсна для мов, сучасніших за Fortran або 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 ), можуть працювати, але можуть бути дуже неефективними.

Переосмислення реалізації рівностей, щоб нерівні об'єкти були "рівними", як правильно заявив Девід Огрен , легко можуть викликати проблеми з технічним обслуговуванням.

І використання Map як явної заміни (як пропонують багато хто), imho робить код менш елегантним.

Якщо мета - отримати доступ до оригінального примірника елемента, що міститься у наборі (сподіваюся, я правильно зрозумів ваш випадок використання), ось ще одне можливе рішення.


Я особисто мав вашу саму потребу під час розробки відеоігра клієнт-сервер з Java. У моєму випадку кожен клієнт мав копії компонентів, що зберігаються на сервері, і проблема була всякий раз, коли клієнту було потрібно змінити об’єкт сервера.

Передача об'єкта через Інтернет означала, що клієнт у будь-якому випадку мав різні екземпляри цього об’єкта. Щоб співставити цей "скопійований" екземпляр з оригіналом, я вирішив використовувати Java UUID.

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

Цей UUID ділиться між клієнтом та екземпляром сервера, тож таким чином їх можна легко порівняти, просто використовуючи Map.

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

З цієї причини я реалізував бібліотеку під назвою MagicSet, яка робить використання Карти «прозорим» для розробника.

https://github.com/ricpacca/magicset


Як і оригінальний Java HashSet, MagicHashSet (який є однією з реалізацій MagicSet, що надається в бібліотеці) використовує резервний HashMap, але замість того, щоб мати елементи як ключі та макетне значення як значення, він використовує UUID елемента як ключ а сам елемент як цінність. Це не викликає накладних витрат у використанні пам'яті порівняно зі звичайним HashSet.

Більше того, MagicSet можна використовувати саме як набір, але з деякими іншими методами, що забезпечують додаткові функціональні можливості, наприклад, 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
}

11

Якщо ваш набір насправді є NavigableSet<Foo>(наприклад, a 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);

Якщо ви зробите лише один, це не буде дуже ефективним, тому що ви будете перебирати всі ваші елементи, але, виконуючи кілька запитів на великому наборі, ви помітите різницю.


5

Чому:

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

Через цей намір / дизайн, якщо потрібно отримати () посилання на збережений об'єкт, а потім його мутувати, можливо, наміри проекту в 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

Ви можете легко реалізувати всі інші методи встановлення.


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інтегрально, це означає, що якщо ви просто скористаєтесь HashMapбезпосередньо і використаєте те саме значення, що і ключ і значення, ви отримаєте потрібний ефект і заощадите собі деяку пам’ять.


1

схоже, належним об'єктом для використання є Interner від guava:

Забезпечує еквівалентну поведінку до String.intern () для інших незмінних типів. Поширені реалізації доступні в класі Interners .

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


0

Тому що будь-яка конкретна реалізація Set може бути, а може і не бути випадковим доступом .

Ви завжди можете отримати ітератор і пройти через Set, використовуючи next()метод ітераторів, щоб повернути потрібний результат, як тільки знайдете рівний елемент. Це працює незалежно від реалізації. Якщо реалізація НЕ є випадковим доступом (зображте набір з підтримкою пов'язаного списку), get(E element)метод в інтерфейсі був би оманливим, оскільки йому доведеться повторити колекцію, щоб знайти елемент, який повертається, і get(E element), здавалося б, це означає, що це буде Потрібно, щоб набір міг перейти безпосередньо до елемента, щоб отримати.

contains() може, а може і не потрібно робити те ж саме, звичайно, залежно від реалізації, але ім'я, схоже, не піддається тому ж непорозумінню.


2
Все, що робитиме метод get (), вже робиться методом містить (). Ви не можете реалізувати містить (), не отримуючи об'єкт, що міститься, і не викликаючи його .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 на кожному з двох об'єктів повинен давати однаковий цілий результат."

Отже, ваше припущення:

"Для уточнення метод рівняння переосмислюється, але він перевіряє лише одне з полів, а не все. Отже, два об'єкти Foo, які вважаються рівними, насправді можуть мати різні значення, тому я не можу просто використовувати foo."

помиляється і ви порушуєте договір. Якщо ми подивимось на метод "містить" інтерфейс Set, то це:

булева містить (Object o);
Повертає істину, якщо цей набір містить вказаний елемент. Більш формально, повертає істину, якщо і лише якщо цей набір містить елемент "e", такий що o == null? e == null: o.equals (e)

Щоб виконати те, що ви хочете, ви можете використовувати Map, де ви визначаєте ключ і зберігаєте свій елемент ключем, який визначає, як об’єкти відрізняються або рівні один одному.


-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]));
   }

-2

Спробуйте використовувати масив:

ObjectClass[] arrayName = SetOfObjects.toArray(new ObjectClass[setOfObjects.size()]);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.