Получение элемента из набора - PullRequest
278 голосов
/ 02 сентября 2011

Почему 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.

Ответы [ 21 ]

0 голосов
/ 20 августа 2017

Если вам нужен 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;
    }
}
0 голосов
/ 09 августа 2017

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

   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]));
   }
0 голосов
/ 06 июля 2017

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

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);   
 }
 }
}
0 голосов
/ 07 марта 2017

Да, используйте HashMap ... но специализированным способом: ловушкой, которую я предвижу при попытке использовать 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 "элемент" для списка)., "member" для набора), как это для ваших членов set (когда это возможно - опять же, область применения класса-оболочки , где этот класс не находится под вашим контролем или уже имеет базовый класс и т. д.), для максимального сокрытия реализации:

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

... использование довольно очевидно (в вашем static фабричном методе *):

SomeClass setMember = new SomeClass( param1, param2 ).addOrGetExisting( set );
0 голосов
/ 14 февраля 2017

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

Map<Integer,Foo> map = Maps.uniqueIndex(fooSet, Foo::getKey);
0 голосов
/ 12 февраля 2014

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

ObjectClass[] arrayName = SetOfObjects.toArray(new ObjectClass[setOfObjects.size()]);
0 голосов
/ 11 февраля 2014

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

http://pastebin.com/Qv6S91n9

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

0 голосов
/ 01 ноября 2013

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

<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();
}
0 голосов
/ 24 мая 2018

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

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

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

0 голосов
/ 10 апреля 2019

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

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

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

...