Bir Kümeden öğe alma


323

Neden Setbaşka bir öğeye eşit bir öğe elde etmek için bir işlem sağlamaz?

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

SetEşdeğer bir eleman içerip içermediğini sorabilirimbar , o zaman neden bu elemanı alamıyorum? :(

Açıklığa kavuşturmak için equalsyöntem geçersiz kılınır, ancak alanların yalnızca birini değil tümünü kontrol eder. FooEşit kabul edilen iki nesnenin aslında farklı değerleri olabilir, bu yüzden kullanamam foo.


2
Bu yazı zaten geniş ölçüde tartışıldı ve iyi cevaplar önerildi. Bununla birlikte, sadece sıralı bir set arıyorsanız, sadece SortedSetharita tabanlı (örneğin TreeSeterişime izin veren first()) ve uygulamalarını kullanın .
Eliran Malka

3
Yukarıda tarif ettiğin durumun aynısı için de bu yöntemi özlüyorum. Objective-C ( NSSet) böyle bir yönteme sahiptir. Çağrılır memberve kümenin içindeki nesneyi memberyöntemin parametresiyle "eşit" olarak karşılaştıran döndürür (elbette farklı bir nesne olabilir ve aynı zamanda farklı özelliklere sahip olabilir, eşit olup olmadığını kontrol edebilir).
Mecki

Yanıtlar:


118

Eşitse öğeyi almanın bir anlamı olmazdı. A bu Mapkullanım için daha uygundur.


Öğeyi hala bulmak istiyorsanız, yineleyiciyi kullanmaktan başka seçeneğiniz yoktur:

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
Elemanı almanın kesinlikle bir anlamı olabilir. Sete eklendikten sonra öğenin bazı değerlerini güncellemek isterseniz ne olur? Örneğin, .equals () yöntemi OP'nin belirttiği gibi tüm alanları kullanmadığında. Öğeyi kaldırmak ve güncellenmiş değerleri ile yeniden eklemek daha az verimli bir çözüm olacaktır.
KyleM

14
Hala MapMap<Foo, Foo>
a'nın

22
@dacwe, buraya geldim çünkü bundan kaçınmanın bir yolunu aramaya başladım! Hem anahtar hem de karşılık gelen değer olarak aynı anda hareket eden bir nesne, bir kümenin tam olarak neyle ilgili olması gerektiğidir. Benim durumumda, bir anahtar kümesinden (String) bazı karmaşık nesne almak istiyorum. Bu Dize, eşlenen nesneye kapsüllenir (ve benzersizdir). Aslında, tüm nesne bahsedilen anahtarın etrafında 'döner'. Ayrıca, arayan adı geçen String'i bilir, ancak nesnenin kendisini bilmez; tam da bu yüzden anahtarla almak istiyor. Şimdi tabii ki bir Harita kullanıyorum, ama garip bir davranış olmaya devam ediyor.
pauluss86

4
@KyleM Kullanım durumunu anlıyorum, ancak hashCode / equals'ın bir parçası olan özelliklere dokunmamanın önemini vurgulamak istiyorum. Set Javadoc'dan: "Not: Değişken nesneler set elemanları olarak kullanılıyorsa çok dikkatli olunmalıdır. Bir nesnenin değeri, nesne bir karşılaştırma varken eşit karşılaştırmaları etkileyecek şekilde değiştirilirse, bir kümenin davranışı belirtilmez öğesi kümesinde. " - Bu nesnelerin değişmez olmasını veya en azından değişmez anahtar özelliklerine sahip olmasını öneririm.
stivlo

5
Map<Foo, Foo>Bir yedek olarak kullanabileceğinizi kabul ediyorum , dezavantajı, bir haritanın her zaman en az bir anahtar ve bir değer depolaması (ve performans için hash'ı da saklaması gerekir), bir set sadece değeri saklamak (ve performans için belki karma). Böylece iyi bir set uygulaması aynı derecede hızlı olabilir, Map<Foo, Foo>ancak% 50'ye kadar daha az bellek kullanır. Java durumunda, HashSet dahili olarak HashMap'e dayalı olduğu için önemli değil.
Mecki

372

"Kesin soruya cevap vermek için Neden vermez Set? Başka unsuru eşittir bir öğe almak için bir operasyon sağlamak", cevap şöyle olacaktır: toplama çerçevesinin tasarımcıları çünkü çok sabırsızlanmıyorum. Çok meşru kullanım durumunuzu tahmin etmediler, safça "matematiksel set soyutlamasını modellemeye" (javadoc'tan) ve sadece yararlı olanları eklemeyi unuttular.get() yöntemi .

Şimdi zımni soruya " öğeyi nasıl elde edersiniz": Bence en iyi çözüm öğeleri kendileriyle eşleştirmek için a Map<E,E>yerine bir kullanmaktır Set<E>. Bu şekilde, bir öğeyi verimli bir şekilde "set" den alabilirsiniz, çünkü will'in get () yöntemi Mapöğeyi verimli bir karma tablo veya ağaç algoritması kullanarak bulacaktır. İsterseniz Set, ek get()yöntemi sunan kendi uygulamanızı yazabilir ,Map .

Aşağıdaki cevaplar bence kötü ya da yanlış:

"Elemanı almanıza gerek yok, çünkü zaten eşit bir nesneniz var": soruda zaten gösterdiğiniz gibi iddia yanlıştır. Eşit olan iki nesne yine de nesne eşitliği ile ilgili olmayan farklı bir duruma sahip olabilir. Amaç, işin içinde yer alan öğenin bu durumuna erişmektir.Set "sorgu" olarak kullanılan nesnenin durumuna değil, .

"Yineleyiciyi kullanmaktan başka seçeneğiniz yok": bu, büyük kümeler için tamamen verimsiz olan bir koleksiyon üzerinde doğrusal bir aramadır (ironik olarak, dahili Setolarak verimli bir şekilde sorgulanabilen karma harita veya ağaç olarak düzenlenir). Yapma! Bu yaklaşımı kullanarak gerçek yaşam sistemlerinde ciddi performans sorunları gördüm. Benim düşünceme göre, eksik get()yöntem hakkında korkunç olan şey o kadar çok çalışmak değil, etrafta çalışmak biraz hantal değil, ancak çoğu programcı, sonuçları düşünmeden doğrusal arama yaklaşımını kullanacak.


27
meh. Eşit olmayan nesnelerin "eşit" olması için eşitlerin uygulanmasını geçersiz kılmak burada problemdir. "Bana bu nesneye özdeş nesneyi getir" diyen bir yöntem istemek ve özdeş olmayan bir nesnenin döndürülmesini beklemek çılgınca ve bakım sorunlarına neden olması kolay görünüyor. Diğerlerinin de belirttiği gibi, bir harita kullanmak tüm bu sorunları çözer: ve yaptığınız şeyi açıklayıcı hale getirir. Eşit olmayan iki nesnenin bir haritada aynı anahtara sahip olabileceğini ve aynı anahtara sahip olmanın aralarındaki ilişkiyi göstereceğini anlamak kolaydır.
David Ogren

20
Güçlü sözler, @David Ogren. Meh? Çılgın? Ancak yorumunuzda, aynı anlama geliyormuş gibi "özdeş" ve "eşit" kelimelerini kullanıyorsunuz. Onlar yapmıyor. Özellikle Java'da kimlik "==" operatörü tarafından ifade edilir ve eşitlik equals () yöntemi ile ifade edilir. Aynı şeyi ifade etselerdi, hiç equals () yöntemine ihtiyaç olmazdı. Diğer dillerde, bu elbette farklı olabilir. Örneğin, Groovy'de kimlik is () yöntemidir ve eşitlik "==" dır. Komik değil mi?
jschreiner

15
Eşdeğer kelimeyi kullanmam gerektiğinde özdeş kelimeyi kullanma konusundaki eleştirileriniz çok geçerli. Ancak bir nesne üzerinde eşitlik tanımlamak, böylece Foo ve Bar "eşit" olmakla birlikte, onları eşit olarak kullanması için "yeterince eşit" değildir, hem işlevsellik hem de okunabilirlik / sürdürülebilirlik ile ilgili her türlü sorunu yaratacaktır. Bu sorun, potansiyel sorunlar için buzdağının sadece ipucunu ayarlayın. Örneğin, eşit nesnelerin eşit karma kodları olmalıdır. Dolayısıyla potansiyel karma çarpışmalara maruz kalacak. Özellikle foo dışında bir şey almak için .get (foo) çağrısına itiraz etmek çılgın mı?
David Ogren

12
Muhtemelen belirtmek gerekir ki, örneğin, HashSet, bir HashMap (anahtarları bir sahte değerle eşler) etrafında bir sarmalayıcı olarak uygulanır. Bu nedenle, açıkça bir HashSet yerine bir HashMap kullanmak bellek kullanımında ek yüke neden olmaz.
Alexey

4
@ user686249 Bunun sadece akademik bir tartışmaya dönüştüğünü hissediyorum. Eşit olmaktan kaçınmaya itiraz ettiğimi kabul ediyorum. Özellikle seninki gibi bir kullanımda. Ancak, hala bu yöntemi çağırma fikrine itirazım var get(). Örneğinizde, customerSet.get (thisCustomer) tarafından çok kafam karışık olurdu. (Oysa, bir Harita, birçok cevap tarafından önerildiği gibi) canonicalCustomerMap.get (bu müşteri) ile iyi olur. Ayrıca daha açık bir şekilde adlandırılmış bir yöntemle (örneğin Njective nesnesindeki Objective-C üye yöntemi gibi) Tamam olurdu.
David Ogren

19

Eşit bir nesneniz varsa, neden setten bir nesneye ihtiyacınız var? Yalnızca bir tuşla "eşit" ise,Map daha iyi bir seçim olacaktır.

Her neyse, aşağıdakiler yapacaktır:

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

Java 8 ile bu bir astar olabilir:

return all.stream().filter(sample::equals).findAny().orElse(null);

Bu cevabı daha iyi sevdim, çünkü OOP'a karşı olan ve Siklomatik Karmaşıklık değerini daha yüksek yapan iki dönüş ifadesini kullanmaktan kaçınırım.
Leo

8
@Leo teşekkürler, ancak tek çıkış paradigması OOP'a karşı değildir ve çoğunlukla Fortran veya COBOL'dan daha modern diller için geçersizdir, ayrıca bkz. Softwareengineering.stackexchange.com/questions/118703/…
Arne Burmeister

1
Küme yerine Harita kullanmak daha iyi bir seçenek gibi görünür: Kümenin öğeleri üzerinde yineleme yapmak, Haritadan tek bir değer elde etmekten daha fazla iştir. (O (N) - O (1))
Jamie Flournoy

@JamieFlournoy doğru, aynı seti farklı öğeler için birden fazla kez kontrol etmeniz gerekiyorsa, bu çok daha iyi. Öncelikle haritayı oluşturmak için daha fazla çaba gerektirdiğinden, tek kullanımlık değil.
Arne Burmeister

18

Kümeyi listeye dönüştürün ve sonra getliste yöntemini kullanın

Set<Foo> set = ...;
List<Foo> list = new ArrayList<Foo>(set);
Foo obj = list.get(0);

37
Bunu anlamıyorum. Bu , kümenin rasgele bir nesnesini alır . Değil nesne.
aioobe

14

Java'daki Varsayılan Set maalesef jschreiner'ın doğru olarak açıkladığı gibi bir "get" işlemi sağlamak için tasarlanmamıştır .

İlgilenilen öğeyi bulmak için ( dacwe tarafından önerilen ) bir yineleyici kullanma veya öğeyi kaldırmak ve güncellenen değerleri ( KyleM tarafından önerilen ) ile yeniden eklemek için çözümler işe yarayabilir, ancak çok verimsiz olabilir.

David Ogren tarafından doğru bir şekilde belirtildiği gibi, eşit olmayan nesnelerin "eşit" olması için eşitlerin uygulanmasının geçersiz kılınması , bakım sorunlarına kolayca neden olabilir.

Ve bir Harita'yı (birçok kişi tarafından önerildiği gibi) açık bir yedek olarak kullanmak imho, kodu daha az zarif hale getirir.

Amaç, sette bulunan öğenin orijinal örneğine erişmekse (kullanım durumunuzu doğru bir şekilde anladığımı umuyoruz), işte başka bir olası çözüm.


Java ile bir istemci-sunucu video oyunu geliştirirken kişisel olarak da aynı ihtiyaçlarınız vardı. Benim durumumda, her istemcinin sunucuda depolanan bileşenlerin kopyaları vardı ve sorun bir istemcinin sunucunun bir nesnesini değiştirmesi gerektiğinde ortaya çıktı.

Bir nesneyi internetten geçirmek, müşterinin o nesnenin zaten farklı örneklerine sahip olması anlamına geliyordu. Bu "kopyalanan" örneği orijinaliyle eşleştirmek için Java UUID'lerini kullanmaya karar verdim.

Bu yüzden, alt sınıflarının her örneğine otomatik olarak rastgele benzersiz bir kimlik veren soyut bir sınıf UniqueItem oluşturdum.

Bu UUID, istemci ve sunucu örneği arasında paylaşılır, böylece bu şekilde bir Harita kullanarak bunları eşleştirmek kolay olabilir.

Bununla birlikte, benzer bir veri tabanında doğrudan bir Harita kullanmak hala yetersizdi. Birisi Harita kullanmanın manevra ve idare etmek için daha karmaşık olabileceğini iddia edebilir.

Bu nedenlerden dolayı Harita'nın kullanımını geliştiriciye "saydam" yapan MagicSet adlı bir kitaplık uyguladım.

https://github.com/ricpacca/magicset


Orijinal Java HashSet gibi, bir MagicHashSet (kütüphanede sağlanan MagicSet uygulamalarından biridir) bir destek HashMap kullanır, ancak anahtar olarak öğelere ve değer olarak kukla bir değere sahip olmak yerine, öğenin UUID değerini anahtar olarak kullanır ve elemanın kendisi değer olarak. Bu, normal HashSet ile karşılaştırıldığında bellek kullanımında ek yüke neden olmaz.

Dahası, bir MagicSet tam olarak bir Set olarak kullanılabilir, ancak getFromId (), popFromId (), removeFromId () vb. Gibi ek işlevler sağlayan bazı yöntemler de kullanılabilir.

Bunu kullanmanın tek şartı, bir MagicSet içinde saklamak istediğiniz herhangi bir öğenin soyut UniqueItem sınıfını genişletmesi gerektiğidir.


Burada, aynı şehrin UUID'sine (hatta yalnızca UUID'sine) sahip başka bir örneği verildiğinde, bir şehrin orijinal örneğini MagicSet'ten almayı hayal eden bir kod örneği.

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

Belirlediğiniz gerçektir a ise NavigableSet<Foo>(örneğin bir olarak TreeSet) ve Foo implements Comparable<Foo>kullanabileceğiniz

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

(@ Eliran-malka'nın ipucu hakkındaki yorumuna teşekkürler.)


5
Kimsenin ilk deliğimi tamamen delirdiğimi düşünerek okumamı önemsemediysem, bu harika bir çözüm olurdu.
Adam

10

Java 8 ile şunları yapabilirsiniz:

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

Ancak dikkatli olun, .get () bir NoSuchElementException oluşturur veya İsteğe bağlı bir öğeyi değiştirebilirsiniz.


5
item->item.equals(theItemYouAreLookingFor)kısaltılabilirtheItemYouAreLookingFor::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);

Sadece bir tane alırsanız, tüm öğeleriniz üzerinde döngü yapacağınız için çok performans göstermeyecektir, ancak büyük bir sette birden fazla geri alma gerçekleştirirken farkı fark edeceksiniz.


5

Neden:

Set'in bir karşılaştırma aracı sağlamada yararlı bir rol oynadığı anlaşılıyor. Yinelenen öğeleri saklamak için tasarlanmamıştır.

Bu niyet / tasarım nedeniyle, eğer biri saklanan nesneye bir referans alacaksa (), onu değiştirecekse, Set'in tasarım amaçlarının engellenmesi ve beklenmedik davranışlara neden olması mümkündür.

Gönderen javadocs

Değişken nesneler ayar elemanları olarak kullanılıyorsa çok dikkatli olunmalıdır. Bir nesnenin değeri, nesne kümedeki bir öğeyken eşit karşılaştırmaları etkileyecek şekilde değiştirilirse, kümenin davranışı belirtilmez.

Nasıl:

Akışlar tanıtıldığına göre, aşağıdakiler yapılabilir

mySet.stream()
.filter(object -> object.property.equals(myProperty))
.findFirst().get();

2

Arrays sınıfını kullanmaya ne dersiniz?

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

çıktı:
öğeler bir, iki



1

Biliyorum, bu uzun zaman önce soruldu ve cevaplandı, ancak kimse ilgileniyorsa, işte benim çözümüm - HashMap tarafından desteklenen özel set sınıfı:

http://pastebin.com/Qv6S91n9

Diğer tüm Set yöntemlerini kolayca uygulayabilirsiniz.


7
Sadece biriyle bağlantı kurmak yerine örneği dahil etmek tercih edilir.
Tüm İşçiler

1

Orada yaptım !! Guava'yı bir haritaya dönüştürmenin hızlı bir yolu kullanıyorsanız:

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

1

Iterator sınıfını kullanabilirsiniz

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

HashSet nth Element istiyorsanız, aşağıdaki çözüm ile gidebilirsiniz, burada HashSet ModelClass nesnesi ekledim.

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

Uygulamanın ilk birkaç satırına bakarsanız java.util.HashSetşunları göreceksiniz:

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

Yani yine de interally HashSetkullanır HashMap, yani sadece HashMapdoğrudan kullanırsanız ve anahtar ve değer ile aynı değeri kullanırsanız, istediğiniz efekti elde edersiniz ve kendinize biraz bellek kaydedersiniz.


1

kullanmak için uygun nesne guava Interner gibi görünüyor :

Diğer değişmez türler için String.intern () öğesine eşdeğer davranış sağlar. Ortak uygulamalar Interners sınıfından edinilebilir .

Ayrıca concurrencyLevel veya kullanılan referans türü gibi çok ilginç birkaç kolu vardır (bir WeakInterner'dan daha yararlı görebildiğim bir SoftInterner sunmadığını belirtmekte fayda olabilir).


0

Çünkü Set'in herhangi bir uygulaması rastgele erişim olabilir veya olmayabilir .

Eşit öğeyi bulduğunuzda istediğiniz sonucu döndürmek için yineleyicilerin yöntemini kullanarak her zaman bir yineleyici alabilir ve Set içinde adım atabilirsiniz next(). Bu, uygulamadan bağımsız olarak çalışır. Uygulama rasgele erişim DEĞİLSE (bağlantılı liste destekli bir Kümenin resmini çizin), get(E element)arabirimdeki bir yöntem aldatıcı olacaktır, çünkü döndürülecek öğeyi bulmak için koleksiyonu yinelemesi gerekir veget(E element) bu, gerekli, Set almak için doğrudan öğeye atlayabilir.

contains() elbette, uygulamaya bağlı olarak aynı şeyi yapmak zorunda kalabilir veya olmayabilir de, ancak isim aynı türden yanlış anlamalara yol açmış gibi görünmüyor.


2
Get () yönteminin yapacağı her şey zaten include () yöntemi tarafından yapılmaktadır. İçerilen nesneyi almadan ve .equals () yöntemini çağırmadan include () yöntemini uygulayamazsınız. API tasarımcılarının, bazı uygulamalarda yavaş olmasına rağmen java.util.List'e get () eklemek konusunda hiçbir derdi yok gibiydi.
Bryan Rink

Bunun doğru olduğunu düşünmüyorum. İki nesne eşittir, ancak == ile aynı olamaz. A nesneniz ve B nesnesi ve A. nesnesini içeren bir set S'niz varsa ve A! = B ise ve B'ye bir referans almak istiyorsanız, referans almak için S.get (A) 'yı çağırabilirsiniz. B, List'in get yönteminin semantiği ile bir get yönteminiz olduğunu varsayarsak, bu S.con (A) (olup olmadığını) içerip içermediğini kontrol etmekten farklı bir kullanım durumudur. Bu, koleksiyonlar için nadir bir kullanım durumu bile değildir.
Tom Tresansky

0

Evet, kullanın HashMap... ama özel bir şekilde: HashMapbir sözde olarak kullanmayı denediğim tuzak Set, "gerçek" öğelerinin Map/Setve "aday" öğelerin, yani bir equalöğesi zaten mevcut. Bu kusursuz değildir, ancak sizi tuzaktan uzaklaştırır:

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

Sonra bunu yapın:

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

Ama ... şimdi candidateprogramcı hemen onu koymadığı sürece bir şekilde kendini imha etmesini Map/Setistiyorsun ... bunu birleştirmek istemezsin contains, candidateböylece onu kullanmadıkça onu kullanmaz Map"anathema ". Belki SomeClassyeni bir Taintablearayüz uygulamak olabilir .

Daha tatmin edici bir çözüm, aşağıdaki gibi bir GettableSet'tir . Bununla birlikte, bunun çalışması SomeClassiçin, tüm kurucuları görünür hale getirmek için tasarımından sorumlu olmalısınız (veya ... bunun için bir sarmalayıcı sınıfı tasarlayıp kullanmaya istekli olmalısınız ):

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
}

Uygulama:

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

Daha NoVisibleConstructorsonra sınıflarınız şöyle görünür:

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, böyle bir NoVisibleConstructorsınıfla ilgili bir teknik sorun : böyle bir sınıfın doğası gereği finalistenmeyen bir itiraz olabilir. Aslında her zaman kukla parametresiz bir protectedkurucu ekleyebilirsiniz :

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

... en azından bir alt sınıfın derlenmesine izin verirdi. Ardından getOrCreate(), alt sınıfa başka bir fabrika yöntemini eklemeniz gerekip gerekmediğini düşünmeniz gerekir .

Son adım , bir liste için soyut bir temel sınıftır (NB "öğesi", bir küme için "üye") set üyeleriniz için bunun gibi (mümkünse - yine, sınıfın kontrolünüz altında olmadığı bir sarıcı sınıf kullanma kapsamı , veya zaten bir temel sınıf, vb. varsa), maksimum uygulama gizleme için:

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

... kullanımı (çoğunlukla içerideki oldukça açıktır SomeClass'ın staticfabrika yöntemiyle):

SomeClass setMember = new SomeClass( param1, param2 ).addOrGetExisting( set );

0

Karma kodun sözleşmesi şunları ortaya koymaktadır:

"İki nesne Object yöntemine göre eşitse, iki nesnenin her birinde hashCode yönteminin çağrılması aynı tamsayı sonucunu üretmelidir."

Yani varsayımınız:

"Açıklığa kavuşturmak için, eşittir yöntemi geçersiz kılınır, ancak alanların tümünü değil, yalnızca birini kontrol eder. Dolayısıyla, eşit kabul edilen iki Foo nesnesinin aslında farklı değerleri olabilir, bu yüzden sadece foo kullanamam."

yanlış ve sözleşmeyi ihlal ediyorsunuz. Set arabiriminin "içerir" yöntemine bakarsak, şunu elde ederiz:

boolean şunları içerir (Object o);
Bu set belirtilen öğeyi içeriyorsa true değerini döndürür. Daha resmi olarak, yalnızca bu kümenin o == null? e == null: o.equals (e)

İstediğinizi gerçekleştirmek için, anahtarı tanımladığınız ve öğelerinizi nesnelerin birbirinden nasıl farklı veya eşit olduğunu tanımlayan anahtarla sakladığınız bir Harita kullanabilirsiniz.


-2

Bu durumu ele alabilecek hızlı yardımcı yöntem:

<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
Bu cevabın, soruyu cevaplamadığı ve hatta herhangi bir şekilde ele alma girişiminde bulunmadığı için çok fazla oy kullanması çok garip.
David Conrad

-2

Aşağıdakiler bir yaklaşım olabilir

   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

Bir dizi kullanmayı deneyin:

ObjectClass[] arrayName = SetOfObjects.toArray(new ObjectClass[setOfObjects.size()]);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.