Ini lebih merupakan respons terhadap Python 3.41 Satu set sebelum ditutup sebagai duplikat.
Yang lain benar: jangan mengandalkan pesanan. Jangan pura-pura ada.
Yang mengatakan, ada satu hal yang dapat Anda andalkan:
list(myset) == list(myset)
Artinya, pesanannya stabil .
Memahami mengapa ada tatanan yang dirasakan membutuhkan pemahaman beberapa hal:
Dari atas:
Sebuah set hash adalah metode menyimpan data acak dengan waktu sangat cepat lookup.
Ini memiliki array dukungan:
# A C array; items may be NULL,
# a pointer to an object, or a
# special dummy object
_ _ 4 _ _ 2 _ _ 6
Kami akan mengabaikan objek boneka khusus, yang ada hanya untuk membuat penghapusan lebih mudah untuk ditangani, karena kami tidak akan menghapus dari set ini.
Untuk mendapatkan pencarian yang sangat cepat, Anda melakukan sihir untuk menghitung hash dari suatu objek. Satu-satunya aturan adalah bahwa dua objek yang sama memiliki hash yang sama. (Tetapi jika dua objek memiliki hash yang sama, mereka bisa tidak sama.)
Anda kemudian membuat indeks dengan mengambil modulus oleh panjang array:
hash(4) % len(storage) = index 2
Ini membuatnya sangat cepat untuk mengakses elemen.
Hash hanya sebagian besar cerita, karena hash(n) % len(storage)
dan hash(m) % len(storage)
dapat menghasilkan jumlah yang sama. Dalam hal ini, beberapa strategi berbeda dapat mencoba dan menyelesaikan konflik. CPython menggunakan "linear probing" 9 kali sebelum melakukan hal-hal yang rumit, sehingga akan terlihat di sebelah kiri slot hingga 9 tempat sebelum mencari di tempat lain.
Kumpulan hash CPython disimpan seperti ini:
Kumpulan hash tidak boleh lebih dari 2/3 penuh . Jika ada 20 elemen dan panjang array 30 elemen, toko dukungan akan diubah ukurannya menjadi lebih besar. Ini karena Anda lebih sering bertabrakan dengan toko dukungan kecil, dan tabrakan memperlambat semuanya.
Toko dukungan ukuran dalam kekuatan 4, mulai dari 8, kecuali untuk set besar (elemen 50k) yang mengubah ukuran dalam kekuatan dua: (8, 32, 128, ...).
Jadi ketika Anda membuat sebuah array, backing store adalah panjang 8. Ketika 5 penuh dan Anda menambahkan elemen, itu akan secara singkat berisi 6 elemen. 6 > ²⁄₃·8
jadi ini memicu pengubahan ukuran, dan backing store empat kali lipat ke ukuran 32.
Akhirnya, hash(n)
hanya mengembalikan n
angka (kecuali -1
yang khusus).
Jadi, mari kita lihat yang pertama:
v_set = {88,11,1,33,21,3,7,55,37,8}
len(v_set)
adalah 10, jadi backing store setidaknya 15 (+1) setelah semua item ditambahkan . Kekuatan yang relevan dari 2 adalah 32. Jadi toko dukungan adalah:
__ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __
Kita punya
hash(88) % 32 = 24
hash(11) % 32 = 11
hash(1) % 32 = 1
hash(33) % 32 = 1
hash(21) % 32 = 21
hash(3) % 32 = 3
hash(7) % 32 = 7
hash(55) % 32 = 23
hash(37) % 32 = 5
hash(8) % 32 = 8
jadi masukkan ini sebagai:
__ 1 __ 3 __ 37 __ 7 8 __ __ 11 __ __ __ __ __ __ __ __ __ 21 __ 55 88 __ __ __ __ __ __ __
33 ← Can't also be where 1 is;
either 1 or 33 has to move
Jadi kami akan mengharapkan pesanan seperti
{[1 or 33], 3, 37, 7, 8, 11, 21, 55, 88}
dengan 1 atau 33 yang tidak di mulai di tempat lain. Ini akan menggunakan linear probing, jadi kita akan memiliki:
↓
__ 1 33 3 __ 37 __ 7 8 __ __ 11 __ __ __ __ __ __ __ __ __ 21 __ 55 88 __ __ __ __ __ __ __
atau
↓
__ 33 1 3 __ 37 __ 7 8 __ __ 11 __ __ __ __ __ __ __ __ __ 21 __ 55 88 __ __ __ __ __ __ __
Anda mungkin berharap 33 menjadi salah satu yang dipindahkan karena 1 sudah ada di sana, tetapi karena ukuran yang terjadi saat set sedang dibangun, ini sebenarnya tidak terjadi. Setiap kali set dibangun kembali, item yang sudah ditambahkan akan disusun ulang secara efektif.
Sekarang Anda bisa melihat alasannya
{7,5,11,1,4,13,55,12,2,3,6,20,9,10}
mungkin dalam rangka. Ada 14 elemen, jadi backing store setidaknya 21 + 1, yang berarti 32:
__ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __
1 hingga 13 hash di 13 slot pertama. 20 masuk dalam slot 20.
__ 1 2 3 4 5 6 7 8 9 10 11 12 13 __ __ __ __ __ __ 20 __ __ __ __ __ __ __ __ __ __ __
55 masuk dalam slot hash(55) % 32
yaitu 23:
__ 1 2 3 4 5 6 7 8 9 10 11 12 13 __ __ __ __ __ __ 20 __ __ 55 __ __ __ __ __ __ __ __
Jika kami memilih 50 sebagai gantinya, kami harapkan
__ 1 2 3 4 5 6 7 8 9 10 11 12 13 __ __ __ __ 50 __ 20 __ __ __ __ __ __ __ __ __ __ __
Dan lihatlah:
{1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 20, 50}
#>>> {1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 50, 20}
pop
diimplementasikan dengan cukup sederhana oleh hal-hal yang terlihat: itu melintasi daftar dan muncul yang pertama.
Ini semua detail implementasi.