Obtendo um elemento de um conjunto

323

Por que não Setfornece uma operação para obter um elemento igual a outro elemento?

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

Posso perguntar se o Setcontém um elemento igual a bar, então por que não consigo obtê-lo? :(

Para esclarecer, o equalsmétodo é substituído, mas apenas verifica um dos campos, não todos. Portanto, dois Fooobjetos considerados iguais podem realmente ter valores diferentes, é por isso que não posso simplesmente usar foo.

foobar
fonte
2
Este post já é amplamente discutido e boas respostas foram sugeridas. No entanto, se você está apenas procurando um conjunto ordenado, basta usar SortedSete suas implementações, que são baseadas em mapas (por exemplo, TreeSetpermite acessar first()).
Eliran Malka
3
Também sinto falta desse método, exatamente no mesmo caso que você descreveu acima. Objective-C ( NSSet) tem esse método. É chamado membere retorna o objeto dentro do conjunto que compara "igual" ao parâmetro do membermétodo (que pode, é claro, ser um objeto diferente e também ter propriedades diferentes, esse igual pode não ser verificado).
Mecki

Respostas:

118

Não faria sentido obter o elemento se ele fosse igual. A Mapé mais adequado para este caso de uso.


Se você ainda deseja encontrar o elemento, não tem outra opção senão usar o iterador:

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
fonte
234
Absolutamente poderia haver um ponto para obter o elemento. E se você deseja atualizar alguns dos valores do elemento depois que ele já foi adicionado ao conjunto? Por exemplo, quando .equals () não usa todos os campos, conforme o OP especificado. Uma solução menos eficiente seria remover o elemento e adicioná-lo novamente com seus valores atualizados.
21413 KyleM
14
Eu ainda diria que uma Mapé mais adequado ( Map<Foo, Foo>neste caso.)
dacwe
22
@ Dacwe, cheguei aqui porque comecei a procurar uma maneira de evitar exatamente isso! Um objeto que age, ao mesmo tempo, como chave e valor correspondente é exatamente o que um conjunto deve ser. No meu caso, eu gostaria de obter algum objeto complexo de um conjunto de chaves (String). Essa String é encapsulada (e exclusiva) para o objeto que está sendo mapeado. De fato, todo o objeto 'gira' em torno da referida chave. Além disso, o chamador conhece a referida String, mas não o objeto em si; é exatamente por isso que ele deseja recuperá-lo por chave. Agora, estou usando um mapa, é claro, mas continua sendo um comportamento estranho.
precisa saber é o seguinte
4
@KyleM Entendo o caso de uso, mas quero enfatizar a importância de não tocar nos atributos que fazem parte do hashCode / igual. No Javadoc do conjunto: "Nota: Deve-se ter muito cuidado se objetos mutáveis ​​forem usados ​​como elementos do conjunto. O comportamento de um conjunto não será especificado se o valor de um objeto for alterado de uma maneira que afete comparações iguais enquanto o objeto é um elemento no conjunto ". - Eu recomendo que esses objetos sejam imutáveis ​​ou, pelo menos, tenham atributos-chave imutáveis.
26615 stivlo
5
Concordo que você pode usar Map<Foo, Foo>como um substituto, a desvantagem é que um mapa sempre deve armazenar pelo menos uma chave e um valor (e para desempenho também deve armazenar o hash), enquanto um conjunto pode se safar apenas armazenando o valor (e talvez hash para desempenho). Portanto, uma boa implementação de conjunto pode ser igualmente rápida, Map<Foo, Foo>mas usa até 50% menos memória. No caso de Java, isso não importa, pois o HashSet é baseado internamente no HashMap de qualquer maneira.
Mecki
372

Para responder à pergunta precisa " Por que não Setfornece uma operação para obter um elemento que se iguala a outro elemento?", A resposta seria: porque os designers da estrutura de coleção não eram muito prospectivos. Eles não anteciparam seu caso de uso legítimo, tentaram ingenuamente "modelar a abstração matemática do conjunto" (do javadoc) e simplesmente esqueceram de adicionar o get()método útil .

Agora, para a pergunta implícita " como você obtém o elemento então": acho que a melhor solução é usar a em Map<E,E>vez de a Set<E>, para mapear os elementos para si mesmos. Dessa forma, você pode recuperar eficientemente um elemento do "conjunto", porque o método get () do Mapencontrará o elemento usando uma tabela de hash eficiente ou um algoritmo de árvore. Se desejar, você pode escrever sua própria implementação Setque oferece o get()método adicional , encapsulando o Map.

As seguintes respostas são ruins ou erradas na minha opinião:

"Você não precisa obter o elemento, porque você já tem um objeto igual": a asserção está errada, como você já mostrou na pergunta. Dois objetos iguais ainda podem ter um estado diferente que não é relevante para a igualdade de objetos. O objetivo é obter acesso a esse estado do elemento contido no Set, não ao estado do objeto usado como uma "consulta".

"Você não tem outra opção senão usar o iterador": é uma pesquisa linear sobre uma coleção que é totalmente ineficiente para conjuntos grandes (ironicamente, internamente, ela Seté organizada como mapa ou árvore de hash que pode ser consultada com eficiência). Não faça isso! Vi sérios problemas de desempenho em sistemas da vida real usando essa abordagem. Na minha opinião, o que é terrível sobre o get()método ausente não é tanto que seja um pouco complicado contorná-lo, mas a maioria dos programadores usará a abordagem de pesquisa linear sem pensar nas implicações.

jschreiner
fonte
27
meh. Substituir a implementação de iguais para que objetos diferentes sejam "iguais" é o problema aqui. Pedir um método que diga "faça com que eu seja o objeto idêntico a esse objeto" e espere que um objeto não idêntico seja retornado parece louco e fácil de causar problemas de manutenção. Como outros sugeriram, o uso de um mapa resolve todos esses problemas: e torna o que você está fazendo auto-explicativo. É fácil entender que dois objetos não iguais podem ter a mesma chave em um mapa e ter a mesma chave mostraria o relacionamento entre eles.
David Ogren
20
Palavras fortes, @ David Ogren. Meh? Louco? Mas no seu comentário, você está usando as palavras "idêntico" e "igual" como se elas significassem a mesma coisa. Eles não. Especificamente, em Java, a identidade é expressa pelo operador "==" e a igualdade é expressa pelo método equals (). Se eles significassem a mesma coisa, não haveria a necessidade de um método equals (). Em outros idiomas, isso pode ser diferente. Por exemplo, no Groovy, a identidade é o método is () e a igualdade é "==". Engraçado, não é?
jschreiner
15
Sua crítica ao meu uso da palavra idêntico quando eu deveria ter usado a palavra equivalente é muito válida. Mas definir iguais em um objeto para que Foo e Bar sejam "iguais", mas não "iguais o suficiente" para ele usá-los equivalentemente, criará todos os tipos de problemas, tanto com a funcionalidade quanto com a legibilidade / manutenção. Esse problema com o Set apenas a ponta do iceberg para possíveis problemas. Por exemplo, objetos iguais devem ter códigos de hash iguais. Então, ele terá potenciais colisões de hash. É louco chamar objetos .get (foo) especificamente para obter algo diferente de foo?
David Ogren
12
Provavelmente vale a pena notar que, por exemplo, o HashSet é implementado como um invólucro em torno de um HashMap (que mapeia as chaves para um valor fictício). Portanto, usar um HashMap explicitamente em vez de um HashSet não causaria sobrecarga no uso da memória.
Alexey B.
4
@ user686249 Sinto que isso se transformou em apenas um debate acadêmico. Eu admito que eu poderia ter exagerado ao objetar a substituir os iguais. Especialmente em um uso como o seu. Mas ainda tenho uma objeção à idéia de chamar esse método get(). No seu exemplo, eu ficaria muito confuso com o customerSet.get (thisCustomer). (Considerando que, como sugerido por muitas respostas), um mapa seria adequado para canonicalCustomerMap.get (esse cliente). Eu também aceitaria um método com um nome mais claro (como o método membro do Objective-C no NSSet).
precisa
19

Se você tem um objeto igual, por que você precisa daquele do conjunto? Se for "igual" apenas por uma chave, uma Mapseria uma escolha melhor.

De qualquer forma, o seguinte será feito:

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

Com o Java 8, isso pode se tornar um liner:

return all.stream().filter(sample::equals).findAny().orElse(null);
Arne Burmeister
fonte
Eu gosto mais dessa resposta, evitaria usar duas declarações de retorno, pois isso é contra o POO e aumenta o valor da complexidade ciclomática.
Leo
8
@Leo obrigado, mas o paradigma de saída única não é contra o OOP e é principalmente inválido para idiomas mais modernos que o Fortran ou o COBOL, consulte também softwareengineering.stackexchange.com/questions/118703/…
Arne Burmeister
1
Usar um mapa em vez de um conjunto parece uma opção melhor: iterar sobre os elementos de um conjunto é mais trabalhoso do que obter um único valor de um mapa. (O (N) vs O (1))
Jamie Flournoy
@JamieFlournoy, certo, se você precisar verificar o mesmo conjunto para elementos diferentes várias vezes, isso é muito melhor. Para uso único, não, pois requer mais esforço para construir o mapa primeiro.
Arne Burmeister
18

Converta o conjunto em lista e use o getmétodo da lista

Set<Foo> set = ...;
List<Foo> list = new ArrayList<Foo>(set);
Foo obj = list.get(0);
Para Kra
fonte
37
Eu não entendo isso. Isso recuperará um objeto arbitrário do conjunto. Não é o objeto.
precisa saber é
14

Infelizmente, o Conjunto Padrão em Java não foi projetado para fornecer uma operação "get", como o jschreiner explicou com precisão.

As soluções de uso de um iterador para encontrar o elemento de interesse (sugerido pelo dacwe ) ou para remover o elemento e adicioná-lo novamente com seus valores atualizados (sugeridos pelo KyleM ), podem funcionar, mas podem ser muito ineficientes.

Substituir a implementação de iguais para que objetos diferentes sejam "iguais", conforme afirmado corretamente por David Ogren , pode facilmente causar problemas de manutenção.

E usar um mapa como uma substituição explícita (como sugerido por muitos), imho, torna o código menos elegante.

Se o objetivo é obter acesso à instância original do elemento contido no conjunto (espero que eu entenda corretamente seu caso de uso), aqui está outra solução possível.


Pessoalmente, tive a mesma necessidade ao desenvolver um videogame cliente-servidor com Java. No meu caso, cada cliente tinha cópias dos componentes armazenados no servidor e o problema era sempre que um cliente precisava modificar um objeto do servidor.

Passar um objeto pela Internet significava que o cliente tinha instâncias diferentes desse objeto de qualquer maneira. Para combinar essa instância "copiada" com a original, decidi usar os UUIDs Java.

Então, eu criei uma classe abstrata UniqueItem, que automaticamente fornece um ID exclusivo aleatório para cada instância de suas subclasses.

Esse UUID é compartilhado entre o cliente e a instância do servidor; portanto, pode ser fácil combiná-los usando um Mapa.

No entanto, o uso direto de um mapa em um caso de uso semelhante ainda era deselegante. Alguém pode argumentar que usar um mapa pode ser mais complicado de manter e manusear.

Por esses motivos, implementei uma biblioteca chamada MagicSet, que torna o uso de um mapa "transparente" para o desenvolvedor.

https://github.com/ricpacca/magicset


Como o Java HashSet original, um MagicHashSet (que é uma das implementações do MagicSet fornecidas na biblioteca) usa um HashMap de backup, mas em vez de ter elementos como chaves e um valor fictício como valores, ele usa o UUID do elemento como chave e o próprio elemento como valor. Isso não causa sobrecarga no uso da memória em comparação com um HashSet normal.

Além disso, um MagicSet pode ser usado exatamente como um conjunto, mas com mais alguns métodos fornecendo funcionalidades adicionais, como getFromId (), popFromId (), removeFromId (), etc.

O único requisito para usá-lo é que qualquer elemento que você deseja armazenar em um MagicSet precisa estender a classe abstrata UniqueItem.


Aqui está um exemplo de código, imaginando recuperar a instância original de uma cidade de um MagicSet, dada outra instância dessa cidade com o mesmo UUID (ou mesmo apenas seu 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
fonte
11

Se o seu conjunto é de fato um NavigableSet<Foo>(como a TreeSet) e Foo implements Comparable<Foo>você pode usar

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

(Obrigado ao comentário de @ eliran-malka pela dica.)

Jesse Glick
fonte
5
Se eu não me importasse que alguém lesse meu código pensando que eu tinha ficado completamente louco, isso seria uma ótima solução.
Adam
10

Com o Java 8, você pode:

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

Mas tenha cuidado, .get () lança uma NoSuchElementException, ou você pode manipular um item opcional.

Tempo nublado
fonte
5
item->item.equals(theItemYouAreLookingFor)pode ser reduzido paratheItemYouAreLookingFor::equals
Henno Vermeulen
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);

Se você conseguir apenas isso, não terá muito desempenho, porque você fará um loop sobre todos os seus elementos, mas ao realizar várias recuperações em um grande conjunto, você notará a diferença.

Xymon
fonte
5

Por quê:

Parece que Set desempenha um papel útil ao fornecer um meio de comparação. Ele foi projetado para não armazenar elementos duplicados.

Devido a essa intenção / design, se for necessário obter () uma referência ao objeto armazenado e depois modificá-lo, é possível que as intenções de design de Set sejam frustradas e possam causar comportamento inesperado.

Do JavaDocs

Deve-se ter muito cuidado se objetos mutáveis ​​forem usados ​​como elementos de conjunto. O comportamento de um conjunto não será especificado se o valor de um objeto for alterado de maneira a afetar comparações iguais enquanto o objeto for um elemento do conjunto.

Quão:

Agora que o Streams foi introduzido, é possível fazer o seguinte

mySet.stream()
.filter(object -> object.property.equals(myProperty))
.findFirst().get();
Jason M
fonte
2

Que tal usar a classe 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);
    }
}

saída:
itens um, dois

velocidade
fonte
1

Eu sei, isso foi perguntado e respondido há muito tempo; no entanto, se alguém estiver interessado, aqui está a minha solução - classe de conjunto personalizado apoiada pelo HashMap:

http://pastebin.com/Qv6S91n9

Você pode implementar facilmente todos os outros métodos Set.

Lukas Z.
fonte
7
É preferível incluir o exemplo em vez de apenas vincular a um.
Todos os trabalhadores têm Essencial
1

Foi lá feito isso !! Se você estiver usando o Guava, uma maneira rápida de convertê-lo em um mapa é:

Map<Integer,Foo> map = Maps.uniqueIndex(fooSet, Foo::getKey);
Heisenberg
fonte
1

você pode usar a classe 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);   
 }
 }
}
chiha asma
fonte
1

Se você quer o enésimo elemento do HashSet, pode seguir a solução abaixo, aqui eu adicionei o objeto ModelClass no 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;
    }
}
Hardik Patel
fonte
1

Se você observar as primeiras linhas da implementação java.util.HashSet, verá:

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

Portanto, HashSetusa-o de HashMapmaneira interativa, o que significa que, se você usar HashMapdiretamente um e usar o mesmo valor da chave e do valor, obterá o efeito desejado e economizará memória.

rghome
fonte
1

parece que o objeto apropriado a ser usado é o Interner da goiaba:

Fornece comportamento equivalente a String.intern () para outros tipos imutáveis. Implementações comuns estão disponíveis na classe Interners .

Ele também possui algumas alavancas muito interessantes, como concurrencyLevel ou o tipo de referência usado (pode ser interessante notar que ele não oferece um SoftInterner que eu poderia considerar mais útil que um WeakInterner).

molyss
fonte
0

Porque qualquer implementação específica do conjunto pode ou não ser acesso aleatório .

Você sempre pode obter um iterador e percorrer o conjunto, usando o next()método dos iteradores para retornar o resultado desejado depois de encontrar o elemento igual. Isso funciona independentemente da implementação. Se a implementação NÃO for de acesso aleatório (imagine um conjunto suportado por lista vinculada), um get(E element)método na interface seria enganoso, pois teria que repetir a coleção para encontrar o elemento a ser retornado, e um get(E element)parecer implicaria que isso seria necessário, que o conjunto possa pular diretamente para o elemento a ser obtido.

contains() pode ou não ter que fazer a mesma coisa, é claro, dependendo da implementação, mas o nome não parece prestar-se ao mesmo tipo de mal-entendidos.

Tom Tresansky
fonte
2
Tudo o que o método get () faria já está sendo feito pelo método contains (). Você não pode implementar contains () sem obter o objeto contido e chamar seu método .equals (). Os designers da API pareciam não ter escrúpulos em adicionar um get () ao java.util.List, embora isso fosse lento em algumas implementações.
Bryan Rink
Eu não acho que isso seja verdade. Dois objetos podem ser iguais via iguais, mas não idênticos via ==. Se você tivesse o objeto A e um conjunto S contendo o objeto B e A.equals (B), mas A! = B e desejasse obter uma referência a B, poderia chamar S.get (A) para obter uma referência a B, supondo que você tenha um método get com a semântica do método get da List, que é um caso de uso diferente do que verificar se S. contém (A) (o que seria). Esse não é um caso de uso tão raro para coleções.
Tom Tresansky
0

Sim, use HashMap... mas de maneira especializada: a armadilha que prevejo ao tentar usar a HashMapcomo pseudo- Seté a possível confusão entre elementos "reais" dos elementos Map/Set"e candidatos", ou seja, elementos usados ​​para testar se um equalelemento já está presente. Isso está longe de ser infalível, mas o afasta da armadilha:

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

Então faça o seguinte:

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

Mas ... agora você quer candidatese autodestruir de alguma forma, a menos que o programador realmente o coloque imediatamente Map/Set... você gostaria containsde "manchar" o candidatemodo que qualquer uso dele, a menos que se junte ao Mapanátema "torna" " Talvez você possa SomeClassimplementar uma nova Taintableinterface.

Uma solução mais satisfatória é um GettableSet , como abaixo. No entanto, para que isso funcione, você deve ser responsável pelo design de SomeClass, a fim de tornar todos os construtores não visíveis (ou ... capazes e dispostos a projetar e usar uma classe de wrapper para ele):

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
}

Implementação:

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 ...
}

Suas NoVisibleConstructoraulas ficam assim:

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: um problema técnico com essa NoVisibleConstructorclasse: pode-se objetar que essa classe seja inerentemente final, o que pode ser indesejável. Na verdade, você sempre pode adicionar um protectedconstrutor fictício sem parâmetros :

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

... que pelo menos deixaria uma subclasse compilar. Você teria que pensar se precisa incluir outro getOrCreate()método de fábrica na subclasse.

A etapa final é uma classe base abstrata (NB "elemento" para uma lista, "membro" para um conjunto) assim para os membros do seu conjunto (quando possível - novamente, o escopo para o uso de uma classe wrapper em que a classe não está sob seu controle, ou já possui uma classe base etc.), para ocultar a implementação máxima:

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

... uso é bastante óbvio (dentro do seu SomeClass's staticmétodo de fábrica):

SomeClass setMember = new SomeClass( param1, param2 ).addOrGetExisting( set );
microfone roedor
fonte
0

O contrato do código hash deixa claro que:

"Se dois objetos são iguais de acordo com o método Object, a chamada do método hashCode em cada um dos dois objetos deve produzir o mesmo resultado inteiro."

Então, sua suposição:

"Para esclarecer, o método equals é substituído, mas apenas verifica um dos campos, não todos. Portanto, dois objetos Foo considerados iguais podem realmente ter valores diferentes, é por isso que não posso simplesmente usar foo".

está errado e você está quebrando o contrato. Se olharmos para o método "contains" da interface Set, temos o seguinte:

booleano contém (Objeto o);
Retorna true se este conjunto contiver o elemento especificado. Mais formalmente, retorna true se e somente se este conjunto contiver um elemento "e" tal que o == null? e == null: o.equals (e)

Para realizar o que você deseja, você pode usar um Mapa no qual define a chave e armazena seu elemento com a chave que define como os objetos são diferentes ou iguais entre si.

jfajunior
fonte
-2

Método de ajuda rápida que pode resolver esta situação:

<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();
}
Chris Willmore
fonte
8
É muito estranho que essa resposta tenha recebido tantos votos positivos, uma vez que não responde à pergunta ou sequer tenta abordá-la de alguma forma.
10249 David
-2

A seguir pode ser uma abordagem

   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
fonte
-2

Tente usar uma matriz:

ObjectClass[] arrayName = SetOfObjects.toArray(new ObjectClass[setOfObjects.size()]);
canni
fonte