Neden java.util.Set get (int dizini) yok?


237

Eminim bunun iyi bir nedeni vardır, ancak birisi neden java.util.Setarayüzün eksik olduğunu get(int Index)veya benzer bir get()yöntemi açıklayabilir mi?

Görünüşe göre setler bir şeyleri koymak için harika, ancak ondan tek bir öğe almanın zarif bir yolunu bulamıyorum.

İlk öğeyi istediğimi biliyorsanız, kullanabilirim set.iterator().next(), ancak aksi takdirde belirli bir dizindeki bir öğeyi almak için bir Array'a yayınlamak zorundayım gibi görünüyor?

Bir kümeden veri almanın uygun yolları nelerdir? (yineleyici kullanmak dışında)

Eminim ki, API'den hariç tutulması, bunu yapmamak için iyi bir neden olduğu anlamına gelir - birisi beni aydınlatabilir mi?

EDIT: Burada son derece harika cevaplar ve birkaç "daha fazla bağlam" diyor. Belirli senaryo, bir sorgudan döndürülen kümenin yalnızca 1 öğeye sahip olduğunu ve bu öğeye erişmeye çalıştığımı makul bir şekilde iddia edebileceğim bir dbUnit sınamasıydı.

Bununla birlikte, soru daha odaklanmış kaldığı için senaryo olmadan daha geçerlidir:

Set ve list arasındaki fark nedir ?

Aşağıdaki harika cevaplar için herkese teşekkürler.


1
Neden bir dizine göre diziden bir öğe alıyorsunuz? Kümeyi sıralı dizi olarak kullanmaya mı çalışıyorsunuz?
MSN

Buradaki özel örnek, hazırda bekletme çağrısından döndürülen bir Kümeye karşı bir dbUnit testidir. Testimde, geri döndürülen nesnenin belirli bir sırada olduğunu varsaymak mantıklıdır, çünkü IDataSet'im bunu ayarlamak için kullandım. Tipik olmayan bir durum, ancak API ile ilgili merakımı yolluyor.
Marty Pitt

1
Belirli bir siparişe bir şeyler eklemek, özel bir Set uygulaması kullanmadığınız sürece bu şekilde kalacağı anlamına gelmez.
Michael Myers

1
"İlk öğeyi istediğimi bilersem, set.iterator (). Next ()" kullanabilirim - Bu satır aslında bir anlam ifade etmiyor. Gerçekten "İlk öğeyi istediğimi bilersem, uygulamanın ilk öğenin tanımına göre, o zaman ..." diyebilirsiniz. Setin kendisi sıralanmamıştır, bu nedenle dizine alınmış erişim anlamlı değildir. Şimdi bir ArrayListSet olsaydı, bu daha mantıklı olurdu (sadece "Liste" ye geç ve mutlu ol). Belki soru için daha fazla bağlam verebilir misiniz?
jsight

Set sırasız değil! Bunun bazı uygulamalarıdır, ancak bazı uygulamalar belirli bir şekilde açıkça sıralanmıştır.
reinierpost

Yanıtlar:


176

Çünkü setlerin sıralaması yoktur. Bazı uygulamalar (özellikle java.util.SortedSetarayüzü uygulayanlar ) yapar, ancak bu kümelerin genel bir özelliği değildir.

Kümeleri bu şekilde kullanmaya çalışıyorsanız, bunun yerine bir liste kullanmayı düşünmelisiniz.


10
@matt b: Hayır, bence düşünmeli. Düşünmek iyidir. ;)
Michael Myers

10
Bir düşünün, sonra yapın.
Joe Phillips

21
"Düşünün" doğru kelime öbeği. İki olası sorun vardır: Bunlardan hangisinin durum olduğunu düşünmek iyidir .
kenj0418

6
Daha basit cevap sıralı bir set kullanmaktır. (Set seçerken tekliğin rol oynadığını varsayıyorum). Ama bir sorum var, SortedSet sipariş edildiğinden, neden API'de get yöntemi yoktur.
uncaught_exceptions

5
@Haydet: Hayır, bir veri yapısının birden çok uygulamasının bir özelliği paylaşması, onu veri yapısının kendisinin bir özelliği yapmaz. Listenin (ArrayList ve Vector) yaygın olarak kullanılan üç uygulamasından ikisi rasgele erişimli olmakla birlikte, rasgele erişimi Listelerin bir özelliği yapmaz.
Michael Myers

74

Aslında bu, Nesne İlişkisel Eşleme kullanan JavaEE uygulamaları (örneğin Hazırda Bekletme ile) yazarken tekrar eden bir sorudur; ve burada cevap veren tüm insanlardan, Andreas Petersson gerçek sorunu anlayan ve doğru cevabı veren tek kişi: Java bir UniqueList'i kaçırıyor! (veya buna OrderedSet veya IndexedSet de diyebilirsiniz).

Maxwing, bu kullanım senaryosundan bahsetti (içinde sıralı VE benzersiz verilere ihtiyacınız var) ve SortedSet'i önerdi, ancak Marty Pitt'in gerçekten ihtiyacı olan bu değil.

Bu "IndexedSet" bir SortedSet ile aynı DEĞİLDİR - bir SortedSet içinde öğeler bir Karşılaştırıcı (veya "doğal" sıralaması kullanılarak) kullanılarak sıralanır.

Ancak bunun yerine, LinkedHashSet'e (başkalarının da önerdiği gibi) daha yakındır ya da (aynı zamanda var olmayan) bir "ArrayListSet" e daha yakındır, çünkü öğelerin eklendikleri sırayla döndürülmesini garanti eder.

Ama LinkedHashSet bir arayüz değil, bir uygulamadır! Gerekli olan bir IndexedSet (veya ListSet veya OrderedSet veya UniqueList) arayüzüdür! Bu, programcının belirli bir sıraya sahip ve kopyaları olmayan bir öğe koleksiyonuna ihtiyaç duyduğunu belirtmesine ve daha sonra herhangi bir uygulamayla (örneğin Hibernate tarafından sağlanan bir uygulama) somutlaştırmasına izin verecektir.

JDK açık kaynaklı olduğundan, belki de bu arayüz nihayet Java 7'ye dahil edilecektir ...


3
Gittiği sürece harika bir cevap, ama bu arada ne yapacağız?
HDave

tabiki öyle. Daha önce hazırda bekleme modunda manytomany ve onetomany ORM olarak liste kullandım. 3'den fazla ilgili varlık içeren bir sol birleştirme sorgusu, bir istisna atılmış bir sorun (veya kusur) bir araya geldi. daha fazla ayrıntı için buraya bakın ( jroller.com/eyallupu/entry/… ). Bu soruna geçici bir çözüm bulmak için, set olarak ORM eşleme koleksiyonu kullanılması gerekir. ama dürüst olmak gerekirse, set programlamada ve bir sipariş koleksiyonuna ihtiyacınız olduğunda uygun değildir. gerçekten ihtiyacımız olan şey "dizinlenmiş set" Sorin Postelnicu'nun söylediği gibi, SIRALAMA ve EŞSİZ
horaceman

2
Apache Commons Collections ListOrderedSetOP'nin 7 yıl önce ihtiyaç duyduğu şeydi (ve bugün ihtiyacım vardı).
Paul

@ Paul: Gerçekten iyi görünen bir şey. Ne yazık ki hala 3 dezavantajı var: 1) Bir sınıf, bir arayüz değil. 2) JDK'da değil. 3) Hazırda Bekletme sorgularının geri dönüşü bu değildir.
Sorin Postelnicu

Evet, ama bu 3 büyük dezavantaj dışında mükemmel! :) Geriye baktığımda, cevabımı değil, soruyu yorumuma göndermeliydim - Kapattım What is needed is an IndexedSet (or ListSet, or OrderedSet, or UniqueList)...ve görmezden geldim ...interface. Bunun için üzgünüm!
Paul

29

Mmyers'ın cevabında bahsedilmeyen bir nokta eklemek .

İlk öğeyi istediğimi biliyorsanız, set.iterator (). Next () kullanabilirim, ancak aksi takdirde belirli bir dizindeki bir öğeyi almak için bir Array'a döküm yapmam gerekir mi?

Bir kümeden veri almanın uygun yolları nelerdir? (yineleyici kullanmak dışında)

Ayrıca, SortedSet(en yaygın uygulaması olan TreeSet) arayüze aşina olmalısınız .

SortedSet, öğelerin doğal sıralaması veya bazılarını kullanarak sıralanan bir Kümedir (yani öğeler benzersizdir) Comparator. İlk ve son öğelere first()ve last()yöntemlerini kullanarak kolayca erişebilirsiniz . A SortedSet, koleksiyonunuzu hem kopyasız hem de belirli bir şekilde sipariş etmeniz gerektiğinde, arada bir kullanışlı hale gelir.

Düzenleme : Öğeleri ekleme sırasında (bir Liste gibi) tutulan bir Kümeye ihtiyacınız varsa, bir göz atın LinkedHashSet.


LinkedHashSet'i kendim seviyorum. Ama evet, bundan bahsetmek güzel. +1
Michael Myers

Teşekkürler, cevabını biraz değiştirdim. (TreeSet'in bazı yönlerini LinkedHashSet ile karıştırdım.)
Jonik

25

Bu tür bir seti ne zaman ve ne zaman bir liste kullanmanız gerektiği sorusuna yol açar. Genellikle tavsiye:

  1. Sipariş verisine ihtiyacınız varsa bir Liste kullanın
  2. Benzersiz verilere ihtiyacınız varsa bir Set kullanın
  3. Her ikisine de ihtiyacınız varsa aşağıdakilerden birini kullanın: bir SortedSet (karşılaştırıcı tarafından sipariş edilen veriler için) veya OrderedSet / UniqueList (ekleme yoluyla sipariş edilen veriler için). Ne yazık ki Java API'sinde henüz OrderedSet / UniqueList bulunmuyor.

Sıklıkla ortaya çıkan dördüncü durum, ikisine de ihtiyacınız olmamasıdır. Bu durumda, bazı programcıların listelerle, bazılarının da setlerle gittiğini görürsünüz. Şahsen ben sipariş vermeden bir liste olarak görmek çok zararlı buluyorum - çünkü bu gerçekten başka bir canavar. Benzersizliği ayarlamak veya eşitliği ayarlamak gibi şeylere ihtiyacınız olmadığı sürece, her zaman listeleri tercih edin.


2
spesifik değilseniz, Koleksiyon <T> ve hatta Tekrarlanabilir <T> öğelerini kabul edin ve Liste olarak başlatın.
Andreas Petersson

Bu bir çanta veya çoklu set olabilir. Ancak Java bunları desteklemez; <T> Koleksiyonunu doğrudan kullanmanız gerektiğini söylüyorlar.
Mekanik salyangoz

4. benzersiz olmayan veri gerekir, ve sipariş umurumda değil. Bir set KULLANAMAZSINIZ. Liste, Çanta veya Çoklu Set çalışacaktır.
Andrew Gallasch

17

Kimsenin tam olarak bu şekilde hecelediğinden emin değilim, ancak aşağıdakileri anlamanız gerekiyor:

Bir kümede "ilk" öğe yoktur.

Çünkü diğerlerinin söylediği gibi setlerin sıralaması yoktur. Küme, özellikle sıralamayı içermeyen matematiksel bir kavramdır.

Tabii ki, bilgisayarınız bellekte sipariş edilmeyen şeylerin bir listesini tutamaz. Bazı emirleri olmalı. Dahili olarak bir dizi veya bağlantılı bir liste veya başka bir şey. Ama bunun ne olduğunu gerçekten bilmiyorsunuz ve gerçekten bir ilk unsuru yok; "ilk" olarak ortaya çıkan öğe bu şekilde tesadüfen ortaya çıkar ve bir dahaki sefere ilk olmayabilir. Belirli bir ilk öğeyi "garanti altına almak" için adımlar atmış olsanız bile, yine de tesadüfen ortaya çıkar, çünkü siz sadece bir Setin belirli bir uygulaması için doğru olana ulaştınız; farklı bir uygulama yaptığınız şeyle bu şekilde çalışmayabilir. Ve aslında, kullandığınız uygulamayı bildiğiniz kadar iyi bilmiyor olabilirsiniz.

İnsanlar bu ALL ile karşılaşırlar. . ZAMAN. RDBMS sistemleri ile ve anlamıyorum. RDBMS sorgusu bir kayıt kümesi döndürür. Bu matematikten aynı tür kümedir: sıralanmamış bir öğe koleksiyonu, sadece bu durumda öğeler kayıttır. Bir RDBMS sorgu sonucunun, ORDER BY deyimini kullanmadığınız sürece hiçbir garantisi yoktur, ancak insanların bunu varsaydığı ve veri veya kodlarının şekli biraz değiştiğinde ve sorgu optimize edicinin çalışmasını tetiklediğinde bir gün kendilerini açma farklı bir şekilde ve aniden sonuçlar bekledikleri sıraya gelmiyor. Bunlar genellikle veritabanı sınıfında (veya belgeleri veya öğreticileri okurken) kendilerine açıklandığında dikkat etmeyen kişilerdir, önlerinde, sorgu sonuçlarının garantili bir sıralaması yoktur.


Heh, ve tabii ki sipariş genellikle kod üretildikten hemen sonra, çok yavaş olduğunda değişir, bu yüzden sorguyu hızlandırmak için bir dizin eklerler. Şimdi kod hızlı çalışıyor, ancak yanlış cevaplar veriyor. Ve kimse üç ya da dört gün boyunca fark etmez ... eğer şanslıysanız. Eğer şanslı değilseniz, kimse bir ay boyunca fark etmez ...
TMN

Bunu özlediğini sanmıyorum (belki de gösterimle özensizdi). Kümedeki ilk unsuru istemiyor, kümeden keyfi bir eleman istiyor. Çünkü Ona keyfi bir eleman verebilir Setolduğunu Iterable.
Elazar Leibovich

Dizine göre get (index) hakkında konuşuyorsunuz. Eşitliğe göre bir get (Object) ne olacak?
Kumar Manish

10

standart java koleksiyonlarında bazı veri yapıları eksik.

Çanta (set gibi ama öğeleri birden çok kez içerebilir)

UniqueList (sıralı liste, her öğeyi yalnızca bir kez içerebilir)

Bu durumda bir uniquelist'e ihtiyacınız var gibi görünüyor

esnek veri yapılarına ihtiyacınız varsa Google Koleksiyonlarıyla ilgileniyor olabilirsiniz


1
Guva bir "UniqueList" sağlıyor mu?
Mike Rylander

Hayır, ancak benzer özelliklere sahip bir java.util.LinkedHashSet öğesine sahip olabilirsiniz.
Andreas Petersson

7

Bu doğru, Set'teki eleman Set Koleksiyonu'nun tanımına göre sıralanmamış. Bu yüzden bir dizinden erişemezler.

Peki neden indeksi parametre olarak sağlayarak değil, aradığımız nesneye eşit olan bir nesneyi elde eden bir get (object) yöntemimiz yok? Bu şekilde, sadece eşit yöntem tarafından kullanılan niteliklerini bilerek, Set içindeki elemanın verilerine erişebiliriz.


7

Bir kümede dizine göre çok sayıda rasgele erişim yapacaksanız, öğelerinin bir dizi görünümünü alabilirsiniz:

Object[] arrayView = mySet.toArray();
//do whatever you need with arrayView[i]

Yine de iki ana dezavantaj var:

  1. Tüm set için bir dizi oluşturulması gerektiğinden bellek verimli değildir.
  2. Küme değiştirilirse, görünüm eski haline gelir.

5

Çünkü Set sadece benzersizliği garanti eder, ancak en uygun erişim veya kullanım kalıpları hakkında hiçbir şey söylemez. Yani, bir Set, her biri çok farklı alma özelliklerine sahip bir Liste veya Harita olabilir.


5

Bir kümede sayısal bir indeks kullanmayı düşünebilmemin tek nedeni yineleme olabilir. Bunun için kullanın

for(A a : set) { 
   visit(a); 
}

Doğru değil, rastgele bir öğeye erişmeye ne dersiniz?
Jeremy Salwen

Ha, ha. iyi bir nokta :) ama bu yanlış kullanım için çok eğilimli olurdu, eminim.
Hugo

3

Ben aslında dizin üzerinden erişim ile bir Sıralı Küme istedim durumlarda koştu (ben bir dizin ile sıralanmamış bir Set erişmenin hiçbir anlam ifade etmez diğer posterler ile aynı fikirde). Örnek olarak çocukların sıralanmasını istediğim bir ağaç olabilir ve yinelenen çocuklara izin verilmezdi.

Onları görüntülemek için dizin üzerinden erişime ihtiyacım vardı ve set öznitelikleri kopyaları verimli bir şekilde ortadan kaldırmak için kullanışlı geldi.

Java.util veya google koleksiyonlarında uygun bir koleksiyon bulamadığımda, bunu kendim uygulayabiliyorum. Temel fikir, bir SortedSet'i sarmak ve indeks yoluyla erişim gerektiğinde bir Liste oluşturmaktır (ve SortedSet değiştiğinde listeyi unutmak). Tabii ki bu sadece sarılmış SortedSet değiştirilirken ve listeye erişmek Koleksiyonun ömrü boyunca ayrıldığında verimli çalışır. Aksi takdirde, sık sıralanan bir liste gibi davranır, yani çok yavaş.

Çok sayıda çocukla, bu, Collections.sort üzerinden sıraladığım bir liste üzerinde performansı çok artırdı.


2

Lütfen endeks yoluyla sadece 2 temel veri yapısına erişilebildiğini unutmayın.

  • Dizi veri yapısına, işlemin O(1)gerçekleştirilmesi için zaman karmaşıklığı ile indeks yoluyla erişilebilir get(int index).
  • LinkedList veri yapısına dizin yoluyla da erişilebilir, ancak işlemin O(n)gerçekleştirilmesi için zaman karmaşıklığı vardır get(int index).

Java'da, Array veri yapısı ArrayListkullanılarak uygulanır .

İken Seti veri yapısı genellikle aracılığıyla uygulanabilir HashTable / HashMap veya BalancedTree hızlı bir eleman vardır ve var olmayan eleman eklemek olmadığını tespit etmek için bir veri yapısı, genellikle iyi uygulanan set elde edebilirsiniz O(1)zaman karmaşıklığı containsişlemi. Java'da, Set'inHashSet en yaygın kullanılan uygulamasıdır, API çağrılarak uygulanır ve bağlantılı listelerle ( Array ve LinkedList kombinasyonu) ayrı zincirleme kullanılarak uygulanır .HashMapHashMap

Yana Seti veri yapısı üzerinden gerçekleştirilebilir, orada hiçbir get(int index)bunun için bir yöntem.


Parmak ağaçları (Haskell'in Data.Sequence.lookupfonksiyonuna bakınız ) ayrıca endeks üzerinden erişime izin verir ( ortadaki O(1)uçlara O(log n)yakın, daha doğru O(min(log(k), log(n-k)))), ayrıca ikili ağaçlar da yapar (Bkz Haskell'in Data.Set.lookupIndexfonksiyonu). Bu nedenle, "Lütfen sadece 2 temel veri yapısına dizin yoluyla erişilebildiğini unutmayın" ifadesinin doğru olmadığı doğru.
noktalı virgül

1

Set arayüzünün nedeni get index tipi bir çağrının veya ilk () veya last () gibi daha temel bir şeye sahip olmamasının nedeni, belirsiz bir işlem olması ve bu nedenle potansiyel olarak tehlikeli bir işlem olmasıdır. Bir yöntem bir Küme döndürürse ve önce () yöntemini çağırırsanız, genel bir Kümenin sipariş konusunda hiçbir garanti vermediği göz önüne alındığında beklenen sonuç nedir? Ortaya çıkan nesne, yöntemin her çağrısı arasında çok iyi değişebilir ya da kullandığınız kütüphane değişiklikler altındaki uygulamayı değiştirene ve şimdi tüm kodunuzun bozulduğunu görene kadar sizi yanlış bir güvenlik hissine sokmayabilir. Özel bir sebep yok.

Burada listelenen geçici çözümlerle ilgili öneriler iyidir. Dizine alınmış erişime ihtiyacınız varsa bir liste kullanın. Yineleyicileri veya toArray'ı genel bir Set ile kullanmaya dikkat edin, çünkü a) sipariş konusunda garanti yoktur ve b) siparişin sonraki çağrılarla veya farklı temel uygulamalarla değişmeyeceğine dair bir garanti yoktur. Arada bir şeye ihtiyacınız varsa, SortedSet veya LinkedHashSet istediğiniz şeydir.

// Keşke Set arayüzünün get-random-elementi olmasını isterdim.


1

java.util.Setsipariş edilmemiş öğelerin bir koleksiyonudur. Set'in bir get (int indeksi) olması mantıklı değildir, çünkü Set'in bir indeksi yoktur ve sadece değeri tahmin edebilirsiniz.

Bunu gerçekten istiyorsanız, Set'ten rastgele öğe almak için bir yöntem kodlayın.


0

Yapabilirsin new ArrayList<T>(set).get(index)


Bu bir Setler Listesi döndürür ve get (index) bir Set döndürür. Aksine, kullandım: new ArrayList<T>(t).get(0) Bir Setten bir indeksle belirli bir eleman alma fikrine geçerli bir karşıtlık olduğunu düşünüyorum. Ancak Set'in, 1 boyutundaki Kümeler için Kümedeki tek öğeye kolay erişim sağlayan tek () üye işlevinin olması iyi olurdu. Bu, yukarıda belirtilenleri kurtaracak new ArrayListveyafor (Foo foo : foos) { return foo; }
Doug Moscrop

0

Kümenin sıralanmasına aldırmazsanız, dizinlenmiş ağaç haritası projesine bir göz atmak isteyebilirsiniz .

Geliştirilmiş TreeSet / TreeMap , öğelere dizine veya bir öğenin dizinini alarak erişim sağlar. Ve uygulama, RB ağacındaki düğüm ağırlıklarının güncellenmesine dayanmaktadır. Yani burada bir liste ile yineleme veya yedekleme yok.


0

Set bir arayüzdür ve bazı uygulama sınıfları HashSet, TreeSet ve LinkedHashSet'tir. Değerleri saklamak için başlık altında HashMap kullanır. HashMap siparişi korumadığından, dizine göre değer elde etmek mümkün değildir.

HashMap bir anahtar, değer çifti sakladığından, ancak Set kullanmadığından Set'in HashMap'ı nasıl kullandığını düşünüyor olmalısınız. geçerli soru. Set'e bir öğe eklediğinizde, dahili olarak, anahtarın Set'e girmek istediğiniz öğe olduğu ve değer kukla sabit olduğu bir HashMap tutar. Aşağıda, add işlevinin dahili bir uygulaması yer almaktadır. Bu nedenle, HashMap'teki tüm anahtarlar aynı sabit değere sahip olacaktır.

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

Tüm Setuygulamaları uygulamaları HashMapsaklamak için başlık altında kullanıyor bu iddiayı doğrulayabilir misiniz TreeSet?
greybeard

1
the keys in the HashMap will have the same constant value HashMapirade anahtarları bir ve aynı değişmezObject
greybeard


-3

Bir Kümedeki öğeyi almak için aşağıdakini kullanın:

public T getElement(Set<T> set, T element) {
T result = null;
if (set instanceof TreeSet<?>) {
    T floor = ((TreeSet<T>) set).floor(element);
    if (floor != null && floor.equals(element))
    result = floor;
} else {
    boolean found = false;
    for (Iterator<T> it = set.iterator(); !found && it.hasNext();) {
    if (true) {
        T current = it.next();
        if (current.equals(element)) {
        result = current;
        found = true;
        }
    }
    }
}
return result;
}

fonksiyon, sorunun sorduğu şey değildir. dizine ihtiyacımız var, değere değil. zaten fonksiyonun ne yapıyor? içindeki bir öğeye eşitse öğeyi döndürdüğü anlaşılıyor. bu ne yapar () içermez?
Janus Troelsen

TTanımlanan nerede ? Neden if (true)?
kuantum
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.