Mengapa tidak java.util.Set punya get (int index)?

237

Saya yakin ada alasan bagus, tetapi bisakah seseorang tolong jelaskan mengapa java.util.Setantarmuka kurang get(int Index), atau get()metode serupa ?

Tampaknya set itu bagus untuk memasukkan berbagai hal, tetapi saya tidak dapat menemukan cara yang elegan untuk mengambil satu item darinya.

Jika saya tahu saya ingin item pertama, saya bisa menggunakan set.iterator().next(), tetapi kalau tidak, sepertinya saya harus melemparkan ke Array untuk mengambil item pada indeks tertentu?

Apa cara yang tepat untuk mengambil data dari suatu set? (selain menggunakan iterator)

Saya yakin fakta bahwa itu dikecualikan dari API berarti ada alasan bagus untuk tidak melakukan ini - bisakah seseorang mencerahkan saya?

EDIT: Beberapa jawaban luar biasa di sini, dan beberapa mengatakan "lebih banyak konteks". Skenario spesifik adalah tes dbUnit, di mana saya dapat dengan wajar menyatakan bahwa set yang dikembalikan dari kueri hanya memiliki 1 item, dan saya mencoba mengakses item itu.

Namun, pertanyaannya lebih valid tanpa skenario, karena tetap lebih fokus:

Apa perbedaan antara set dan daftar .

Terima kasih untuk semua jawaban fantastis di bawah ini.

Marty Pitt
sumber
1
Mengapa Anda mendapatkan elemen dari set dengan indeks? Apakah Anda mencoba menggunakan set sebagai array yang diurutkan?
MSN
Contoh khusus di sini adalah tes dbUnit terhadap Set yang dikembalikan dari panggilan hibernasi. Dalam pengujian saya, masuk akal untuk berasumsi (karena saya tegaskan) bahwa objek yang dikembalikan dalam urutan tertentu, karena IDataSet saya, saya biasa mengaturnya. Ini adalah kasus yang tidak umum, tetapi mengarah ke keingintahuan saya tentang API.
Marty Pitt
1
Menambahkan hal-hal dalam urutan tertentu tidak berarti mereka akan tetap seperti itu, kecuali jika Anda menggunakan implementasi Set kustom.
Michael Myers
1
"Jika saya tahu saya ingin item pertama, saya dapat menggunakan set.iterator (). Next ()" - Baris ini sebenarnya tidak masuk akal. Anda benar-benar mengatakan "Jika saya tahu saya ingin item pertama, dengan definisi implementasi dari item pertama, maka saya bisa ...". Atur sendiri tidak teratur, sehingga akses yang diindeks tidak masuk akal. Sekarang jika ada ArrayListSet, itu akan lebih masuk akal (cukup masukkan ke "Daftar" dan berbahagialah). Mungkin Anda bisa memberikan lebih banyak konteks untuk pertanyaan itu?
jsight
Set tidak dibatalkan perintah! Implementasi tertentu itu, tetapi beberapa implementasi secara eksplisit dipesan dengan cara tertentu.
reinierpost

Jawaban:

176

Karena set tidak memiliki pemesanan. Beberapa implementasi memang (terutama yang mengimplementasikan java.util.SortedSetantarmuka), tetapi itu bukan properti umum set.

Jika Anda mencoba menggunakan set dengan cara ini, Anda sebaiknya mempertimbangkan untuk menggunakan daftar.

Michael Myers
sumber
10
@ tikar b: Tidak, saya pikir dia harus mempertimbangkannya. Berpikir itu baik. ;)
Michael Myers
10
Pertimbangkan, lalu lakukan.
Joe Phillips
21
"Pertimbangkan" adalah ungkapan yang benar. Ada dua kemungkinan masalah (a) Dia menggunakan set ketika dia harus menggunakan sesuatu yang lain, atau (b) Dia mencoba melakukan hal-hal dengan Set yang tidak mereka dukung tetapi dia bisa melakukan cara yang berbeda. Adalah baik untuk mempertimbangkan yang mana dari kasus ini.
kenj0418
6
Mungkin jawaban yang lebih sederhana adalah dengan menggunakan set yang diurutkan. (Saya menganggap keunikan memainkan peran saat memilih set). Tapi saya punya pertanyaan, karena SortedSet dipesan, mengapa tidak ada metode get dalam api.
uncaught_exceptions
5
@ HDave: Tidak, fakta bahwa beberapa implementasi dari struktur data berbagi properti tidak menjadikannya properti dari struktur data itu sendiri. Dua dari tiga implementasi Daftar (ArrayList dan Vektor) yang umum digunakan adalah akses acak, tetapi itu tidak membuat akses acak menjadi properti Daftar.
Michael Myers
74

Sebenarnya ini adalah pertanyaan berulang ketika menulis aplikasi JavaEE yang menggunakan Pemetaan Objek-Relasional (misalnya dengan Hibernate); dan dari semua orang yang menjawab di sini, Andreas Petersson adalah satu-satunya yang memahami masalah sebenarnya dan menawarkan jawaban yang benar untuk itu: Jawa kehilangan UniqueList! (atau Anda juga dapat menyebutnya OrderedSet, atau IndexedSet).

Maxwing menyebutkan use-case ini (di mana Anda perlu memesan DAN data unik) dan dia menyarankan SortedSet, tapi ini bukan yang benar-benar dibutuhkan Marty Pitt.

"IndexedSet" ini TIDAK sama dengan SortedSet - dalam SortedSet elemen diurutkan dengan menggunakan Comparator (atau menggunakan pemesanan "natural" mereka).

Tetapi sebaliknya lebih dekat ke LinkedHashSet (yang orang lain juga menyarankan), atau bahkan lebih ke "ArrayListSet" (juga tidak ada), karena menjamin bahwa elemen dikembalikan dalam urutan yang sama seperti yang dimasukkan.

Tetapi LinkedHashSet adalah implementasi, bukan antarmuka! Yang diperlukan adalah antarmuka IndexedSet (atau ListSet, atau OrderedSet, atau UniqueList)! Ini akan memungkinkan programmer untuk menentukan bahwa ia membutuhkan kumpulan elemen yang memiliki urutan tertentu dan tanpa duplikat, dan kemudian instantiate dengan implementasi apa pun (misalnya implementasi yang disediakan oleh Hibernate).

Karena JDK adalah open-source, mungkin antarmuka ini akhirnya akan dimasukkan dalam Java 7 ...

Sorin Postelnicu
sumber
3
Sejauh ini jawabannya bagus, tapi apa yang kita lakukan sementara itu?
HDave
tentu saja. saya menggunakan daftar manytomany dan onetomany ORM di hibernate sebelumnya. saya mengalami masalah (atau cacat) saat kueri bergabung kiri yang melibatkan lebih dari 3 entitas terkait, pengecualian dilemparkan. lihat di sini untuk detail lebih lanjut ( jroller.com/eyallupu/entry/… ). untuk mengatasi masalah ini, gunakan set sebagai koleksi pemetaan ORM diperlukan. tetapi jujur ​​untuk mengatakan, set tidak nyaman untuk mengakses dalam pemrograman, dan juga ketika Anda membutuhkan koleksi pemesanan. yang benar-benar kita butuhkan adalah "indexedset" seperti yang dikatakan Sorin Postelnicu, SORT dan UNIK
horaceman
2
Koleksi Apache Commons memiliki ListOrderedSetapa yang dibutuhkan OP 7 tahun lalu (dan saya butuhkan hari ini).
Paul
@ Paul: Itu memang sesuatu yang terlihat sangat bagus. Sayangnya masih memiliki 3 kelemahan: 1) Ini adalah kelas, bukan antarmuka. 2) Tidak ada di JDK. 3) Bukan apa yang dikembalikan pertanyaan Hibernate.
Sorin Postelnicu
Ya, tapi selain 3 kelemahan utama itu sempurna! :) Dalam retrospeksi saya seharusnya memposting komentar saya untuk pertanyaan dan bukan jawaban Anda - saya mengunci What is needed is an IndexedSet (or ListSet, or OrderedSet, or UniqueList)...dan mengabaikan ...interface. Maaf soal itu!
Paul
29

Hanya menambahkan satu poin yang tidak disebutkan dalam jawaban mmyers .

Jika saya tahu saya ingin item pertama, saya dapat menggunakan set.iterator (). Next (), tetapi jika tidak, sepertinya saya harus melemparkan ke Array untuk mengambil item pada indeks tertentu?

Apa cara yang tepat untuk mengambil data dari suatu set? (selain menggunakan iterator)

Anda juga harus membiasakan diri dengan SortedSetantarmuka (implementasi yang paling umum adalah TreeSet).

SortedSet adalah Set (yaitu elemen yang unik) yang disimpan dengan urutan alami elemen atau menggunakan beberapa Comparator. Anda dapat dengan mudah mengakses item pertama dan terakhir menggunakan first()dan last()metode. A SortedSetsangat berguna sesekali, ketika Anda perlu menjaga koleksi Anda bebas dari duplikat dan dipesan dengan cara tertentu.

Sunting : Jika Anda membutuhkan Set yang unsur-unsurnya disimpan dalam urutan penyisipan (seperti Daftar), lihatlah LinkedHashSet.

Jonik
sumber
Saya suka LinkedHashSet sendiri. Tapi ya, ini bagus untuk disebutkan. +1
Michael Myers
Terima kasih, saya mengubah sedikit jawabannya. (Sepertinya saya punya beberapa aspek TreeSet bingung dengan orang-orang dari LinkedHashSet.)
Jonik
25

Jenis ini mengarah ke pertanyaan kapan Anda harus menggunakan set dan kapan Anda harus menggunakan daftar. Biasanya, sarannya:

  1. Jika Anda memerlukan data yang dipesan, gunakan Daftar
  2. Jika Anda membutuhkan data unik, gunakan Set
  3. Jika Anda membutuhkan keduanya, gunakan: a SortedSet (untuk data yang dipesan oleh pembanding) atau OrderedSet / UniqueList (untuk data yang dipesan dengan penyisipan). Sayangnya Java API belum memiliki OrderedSet / UniqueList.

Kasus keempat yang sering muncul adalah Anda tidak membutuhkan keduanya. Dalam hal ini Anda melihat beberapa pemrogram pergi dengan daftar dan beberapa dengan set. Secara pribadi saya merasa sangat berbahaya untuk melihat ditetapkan sebagai daftar tanpa memesan - karena itu benar-benar binatang buas lainnya. Kecuali Anda membutuhkan hal-hal seperti mengatur keunikan atau mengatur kesetaraan, selalu nikmatilah daftar.

waxwing
sumber
2
jika Anda tidak spesifik, terima Koleksi <T> atau bahkan Iterable <T> dan inisialisasi sebagai Daftar.
Andreas Petersson
Ini akan menjadi tas atau multiset. Tetapi Java tidak mendukung itu; mereka mengatakan Anda harus menggunakan Koleksi <T> secara langsung.
Siput mekanik
4. Anda membutuhkan data yang tidak unik, dan tidak peduli tentang pesanan. Anda TIDAK BISA menggunakan set. Daftar, Tas atau Multiset akan berfungsi.
Andrew Gallasch
17

Saya tidak yakin apakah ada yang mengatakannya dengan cara ini, tetapi Anda perlu memahami yang berikut:

Tidak ada elemen "pertama" dalam set.

Karena, seperti yang dikatakan orang lain, set tidak memiliki urutan. Satu set adalah konsep matematika yang secara khusus tidak termasuk pemesanan.

Tentu saja, komputer Anda tidak dapat menyimpan daftar barang yang tidak dipesan dalam memori. Itu harus memiliki beberapa pemesanan. Secara internal itu adalah array atau daftar yang terhubung atau sesuatu. Tetapi Anda tidak benar-benar tahu apa itu, dan tidak benar-benar memiliki elemen pertama; elemen yang keluar "pertama" keluar seperti itu secara kebetulan, dan mungkin bukan yang pertama kali. Bahkan jika Anda mengambil langkah-langkah untuk "menjamin" elemen pertama tertentu, itu masih keluar secara kebetulan, karena Anda kebetulan melakukannya dengan benar untuk satu implementasi tertentu dari Set; implementasi yang berbeda mungkin tidak bekerja seperti itu dengan apa yang Anda lakukan. Dan, pada kenyataannya, Anda mungkin tidak tahu implementasi yang Anda gunakan sebaik yang Anda kira.

Orang-orang mengalami SEMUA ini. ITU. WAKTU. dengan sistem RDBMS dan tidak mengerti. Kueri RDBMS mengembalikan serangkaian catatan. Ini adalah jenis himpunan yang sama dari matematika: koleksi item yang tidak teratur, hanya dalam hal ini item tersebut adalah catatan. Hasil kueri RDBMS sama sekali tidak memiliki jaminan pesanan kecuali Anda menggunakan klausa ORDER BY, tetapi setiap saat orang menganggapnya dan kemudian naik sendiri suatu hari ketika bentuk data atau kode mereka sedikit berubah dan memicu pengoptimal permintaan untuk bekerja dengan cara yang berbeda dan tiba-tiba hasilnya tidak keluar dalam urutan yang mereka harapkan. Ini biasanya adalah orang-orang yang tidak memperhatikan di kelas basis data (atau ketika membaca dokumentasi atau tutorial) ketika dijelaskan kepada mereka, di muka, bahwa hasil permintaan tidak memiliki pemesanan yang dijamin.

skiphoppy
sumber
Heh, dan tentu saja pemesanan biasanya berubah tepat setelah kode masuk ke produksi, ketika terlalu lambat, sehingga mereka menambahkan indeks untuk mempercepat permintaan. Sekarang kode berjalan cepat, tetapi memberikan jawaban yang salah. Dan tidak ada yang memperhatikan selama tiga atau empat hari ... jika Anda beruntung. Jika Anda tidak beruntung, tidak ada yang memperhatikan selama sebulan ...
TMN
Saya tidak berpikir dia melewatkan itu (mungkin dia ceroboh dengan notasi). Dia tidak ingin elemen pertama dari set, dia ingin elemen yang sewenang-wenang dari set. Anda dapat memberinya elemen sewenang-wenang sejak Setitu Iterable.
Elazar Leibovich
Anda berbicara tentang get (indeks) dengan indeks. Bagaimana dengan mendapatkan (Obyek) dengan kesetaraan?
Kumar Manish
10

beberapa struktur data hilang dari koleksi java standar.

Tas (seperti set tetapi dapat mengandung elemen beberapa kali)

UniqueList (daftar berurutan, dapat memuat setiap elemen hanya satu kali)

Sepertinya Anda perlu uniquelist dalam hal ini

jika Anda membutuhkan struktur data yang fleksibel, Anda mungkin tertarik dengan Google Collections

Andreas Petersson
sumber
1
Apakah Guva menyediakan "UniqueList"?
Mike Rylander
tidak, tetapi Anda dapat memiliki java.util.LinkedHashSet yang memiliki properti serupa.
Andreas Petersson
7

Itu benar, elemen di Set tidak diurutkan, menurut definisi Set Collection. Jadi mereka tidak dapat diakses oleh indeks.

Tetapi mengapa kita tidak memiliki metode get (objek), bukan dengan memberikan indeks sebagai parameter, tetapi objek yang sama dengan yang kita cari? Dengan cara ini, kita dapat mengakses data elemen di dalam Set, hanya dengan mengetahui atributnya yang digunakan oleh metode yang sama.

dinding
sumber
7

Jika Anda akan melakukan banyak akses acak dengan indeks dalam satu set, Anda bisa mendapatkan tampilan array dari elemen-elemennya:

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

Ada dua kelemahan utama:

  1. Ini bukan memori efisien, karena array untuk seluruh set perlu dibuat.
  2. Jika set diubah, tampilan menjadi usang.
fortran
sumber
5

Itu karena Set hanya menjamin keunikan, tetapi tidak mengatakan apa pun tentang akses optimal atau pola penggunaan. Yaitu, Set dapat berupa Daftar atau Peta, yang masing-masing memiliki karakteristik pengambilan yang sangat berbeda.

jsight
sumber
5

Satu-satunya alasan saya bisa memikirkan untuk menggunakan indeks numerik dalam set adalah untuk iterasi. Untuk itu, gunakan

for(A a : set) { 
   visit(a); 
}
Hugo
sumber
Tidak benar, bagaimana dengan mengakses elemen acak?
Jeremy Salwen
Ha ha. Poin bagus :) tapi itu akan sangat rentan terhadap penyalahgunaan, saya yakin.
Hugo
3

Saya mengalami situasi di mana saya benar-benar ingin diurutkan Set dengan akses melalui indeks (saya setuju dengan poster lain yang mengakses Set yang tidak disortir dengan indeks tidak masuk akal). Contohnya adalah pohon di mana saya ingin anak-anak disortir dan duplikat anak-anak tidak diizinkan.

Saya membutuhkan akses melalui indeks untuk menampilkannya dan atribut yang ditetapkan berguna untuk secara efisien menghilangkan duplikat.

Tidak menemukan koleksi yang cocok di koleksi java.util atau google, saya merasa mudah untuk mengimplementasikannya sendiri. Ide dasarnya adalah untuk membungkus SortedSet dan membuat Daftar ketika akses melalui indeks diperlukan (dan lupakan daftar ketika SortedSet diubah). Ini tentu saja hanya bekerja secara efisien ketika mengubah SortedSet yang dibungkus dan mengakses daftar dipisahkan dalam masa koleksi. Kalau tidak, ia berperilaku seperti daftar yang sering diurutkan, yaitu terlalu lambat.

Dengan sejumlah besar anak-anak, kinerja ini jauh lebih baik daripada daftar yang saya simpan diurutkan melalui Collections.sort.

buchweizen
sumber
2

Harap dicatat hanya 2 struktur data dasar yang dapat diakses melalui indeks.

  • Struktur data array dapat diakses melalui indeks dengan O(1)kompleksitas waktu yang harus dicapaiget(int index) operasi.
  • Struktur data LinkedList juga dapat diakses melalui indeks, tetapi dengan O(n)kompleksitas waktu untuk mencapai get(int index)operasi.

Di Jawa, ArrayListdiimplementasikan menggunakan struktur data Array .

Sementara Set struktur data biasanya dapat diimplementasikan melalui HashTable / HashMap atau BalancedTree struktur data, untuk cepat mendeteksi apakah sebuah elemen ada dan menambahkan non-ada elemen, biasanya diterapkan dengan baik Set dapat mencapai O(1)waktu kompleksitas containsoperasi. Di Jawa, HashSetadalah implementasi Set yang paling umum digunakan , diimplementasikan dengan memanggil HashMapAPI, dan HashMapdiimplementasikan menggunakan rantai terpisah dengan daftar tertaut (kombinasi dari Array dan LinkedList ).

Karena Set dapat diimplementasikan melalui struktur data yang berbeda, tidak ada get(int index)metode untuk itu.

coderz
sumber
Pohon jari (Lihat Data.Sequence.lookupfungsi Haskell ) juga memungkinkan akses melalui indeks ( O(1)dekat ujungnyaO(log n) dekat tengah, lebih akurat O(min(log(k), log(n-k)))), juga pohon biner melakukannya juga (Lihat Data.Set.lookupIndexfungsi Haskell ). Jadi pernyataan awal Anda bahwa "Harap dicatat hanya 2 struktur data dasar yang dapat diakses melalui indeks" tidak benar.
titik koma
1

Alasan mengapa antarmuka Set tidak memiliki panggilan tipe indeks atau bahkan sesuatu yang lebih mendasar, seperti first () atau last (), adalah karena ia merupakan operasi yang ambigu, dan karenanya merupakan operasi yang berpotensi berbahaya. Jika suatu metode mengembalikan Set, dan Anda memanggil, katakan metode first () di atasnya, apa hasil yang diharapkan, mengingat bahwa Set generik tidak membuat jaminan pada pemesanan? Objek yang dihasilkan bisa sangat bervariasi antara setiap panggilan metode, atau mungkin tidak dan membuai Anda ke rasa aman yang salah, sampai perpustakaan Anda menggunakan perubahan mengubah implementasi di bawahnya dan sekarang Anda menemukan bahwa semua kode Anda istirahat untuk tidak ada alasan khusus.

Saran tentang pemecahan masalah yang tercantum di sini bagus. Jika Anda memerlukan akses yang diindeks, gunakan daftar. Berhati-hatilah dengan menggunakan iterator atau toArray dengan Perangkat generik, karena a) tidak ada jaminan pada pemesanan dan b) tidak ada jaminan bahwa pemesanan tidak akan berubah dengan pemanggilan berikutnya atau dengan implementasi mendasar yang berbeda. Jika Anda membutuhkan sesuatu di antaranya, SortedSet atau LinkedHashSet adalah yang Anda inginkan.

// Aku berharap antarmuka Set memiliki elemen get-random.

Dan
sumber
1

java.util.Setadalah koleksi barang yang tidak dipesan. Tidak masuk akal jika Set memiliki get (int index), karena Set tidak memiliki indeks dan Anda hanya bisa menebak nilainya.

Jika Anda benar-benar menginginkan ini, kode metode untuk mendapatkan elemen acak dari Set.

Hasil Pencarian Hasil Web Pi
sumber
0

Anda dapat melakukan new ArrayList<T>(set).get(index)

Janus Troelsen
sumber
Ini mengembalikan Daftar Set dan mendapatkan (indeks) mengembalikan Set. Sebaliknya, saya menggunakan: new ArrayList<T>(t).get(0) Saya pikir ada oposisi yang valid terhadap gagasan untuk mendapatkan elemen tertentu dari Set oleh indeks. Tapi alangkah baiknya jika Set memiliki fungsi anggota only () yang, untuk Set ukuran 1, memberikan akses mudah ke satu-satunya elemen dalam Set. Ini akan menyelamatkan yang disebutkan di atas new ArrayListataufor (Foo foo : foos) { return foo; }
Doug Moscrop
0

Jika Anda tidak keberatan set akan diurutkan maka Anda mungkin tertarik untuk melihat indeks-pohon-peta .

TreeSet / TreeMap yang disempurnakan menyediakan akses ke elemen dengan indeks atau mendapatkan indeks suatu elemen. Dan implementasinya didasarkan pada pembaruan bobot simpul di pohon RB. Jadi tidak ada iterasi atau cadangan dengan daftar di sini.

Vitaly Sazanovich
sumber
0

Set adalah antarmuka dan beberapa kelas implementasinya adalah HashSet, TreeSet dan LinkedHashSet. Ini menggunakan HashMap di bawah tenda untuk menyimpan nilai. Karena HashMap tidak mempertahankan pesanan, tidak mungkin untuk mendapatkan nilai berdasarkan indeks.

Anda sekarang harus berpikir bagaimana Set menggunakan HashMap karena HashMap menyimpan pasangan kunci, nilai tetapi Set tidak. pertanyaan yang valid. ketika Anda menambahkan elemen di Set, secara internal, ia mempertahankan HashMap di mana kuncinya adalah elemen yang ingin Anda masukkan di Set dan nilainya adalah konstanta dummy. Di bawah ini adalah implementasi internal fungsi add. Karenanya, semua kunci di HashMap akan memiliki nilai konstan yang sama.

// 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;
}
magnonim
sumber
Semua Setimplementasi digunakan di HashMapbawah tenda untuk menyimpan nilai, dapatkah Anda membuktikan klaim itu TreeSet?
greybeard
1
the keys in the HashMap will have the same constant value kunci di HashMapakan memetakan ke satu dan sama abadiObject
greybeard
-3

Untuk mendapatkan elemen dalam Set, saya menggunakan yang berikut:

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;
}
La la
sumber
fungsi bukan apa yang ditanyakan. kita perlu indeks, bukan nilainya. apa fungsi Anda lakukan? Sepertinya itu hanya mengembalikan elemen jika itu sama dengan elemen di dalamnya. apa artinya ini mengandung () tidak?
Janus Troelsen
Di mana yang Tdidefinisikan? Mengapa if (true)?
kuantum