Konstruktor biasa ArrayList
adalah:
ArrayList<?> list = new ArrayList<>();
Tetapi ada juga konstruktor yang kelebihan muatan dengan parameter untuk kapasitas awalnya:
ArrayList<?> list = new ArrayList<>(20);
Mengapa berguna untuk membuat ArrayList
dengan kapasitas awal ketika kita dapat menambahkannya sesuka kita?
java
data-structures
arraylist
capacity
rampok
sumber
sumber
Jawaban:
Jika Anda tahu sebelumnya apa ukuran yang
ArrayList
akan terjadi, akan lebih efisien untuk menentukan kapasitas awal. Jika Anda tidak melakukan ini, array internal harus berulang kali dialokasikan kembali seiring bertambahnya daftar.Semakin besar daftar akhir, semakin banyak waktu yang Anda hemat dengan menghindari realokasi.
Yang mengatakan, bahkan tanpa pra-alokasi, memasukkan
n
elemen di belakang sebuahArrayList
dijamin akan memakanO(n)
waktu total . Dengan kata lain, menambahkan elemen adalah operasi waktu konstan yang diamortisasi. Ini dicapai dengan meminta setiap realokasi meningkatkan ukuran array secara eksponensial, biasanya dengan faktor1.5
. Dengan pendekatan ini, jumlah total operasi dapat ditunjukkanO(n)
.sumber
O(n log n)
akan melakukan waktulog n
kerjan
. Itu perkiraan terlalu tinggi (meskipun secara teknis benar dengan O besar karena itu menjadi batas atas). Ini menyalin s + s * 1,5 + s * 1,5 ^ 2 + ... + s * 1,5 ^ m (sedemikian sehingga s * 1,5 ^ m <n <s * 1,5 ^ (m + 1)) elemen secara total. Saya tidak pandai dalam jumlah jadi saya tidak bisa memberi Anda matematika yang tepat dari atas kepala saya (untuk mengubah ukuran faktor 2, ini 2n, jadi mungkin 1,5n memberi atau mengambil konstanta kecil), tetapi itu tidak Jangan terlalu menyipitkan mata untuk melihat bahwa jumlah ini paling banyak merupakan faktor konstan yang lebih besar dari n. Jadi dibutuhkan O (k * n) salinan, yang tentu saja O (n).Karena
ArrayList
adalah struktur data array yang mengubah ukuran secara dinamis , yang berarti diimplementasikan sebagai array dengan ukuran tetap awal (default). Ketika ini terisi, array akan diperpanjang menjadi satu ukuran ganda. Operasi ini mahal, jadi Anda ingin sesedikit mungkin.Jadi, jika Anda tahu batas atas Anda adalah 20 item, maka menciptakan array dengan panjang awal 20 lebih baik daripada menggunakan default, katakanlah, 15 dan kemudian ubah ukurannya
15*2 = 30
dan gunakan hanya 20 saat membuang-buang siklus ekspansi.PS - Seperti yang dikatakan AmitG, faktor ekspansi adalah implementasi spesifik (dalam hal ini
(oldCapacity * 3)/2 + 1
)sumber
int newCapacity = (oldCapacity * 3)/2 + 1;
Ukuran standar Arraylist adalah 10 .
Jadi jika Anda akan menambah 100 atau lebih catatan, Anda dapat melihat overhead realokasi memori.
Jadi jika Anda memiliki gagasan tentang jumlah elemen yang akan disimpan di Arraylist lebih baik untuk membuat Arraylist dengan ukuran itu daripada mulai dengan 10 dan kemudian meningkatkannya.
sumber
private static final int DEFAULT_CAPACITY = 10
Saya sebenarnya menulis posting blog pada topik 2 bulan yang lalu. Artikel ini untuk C #
List<T>
tetapi JavaArrayList
memiliki implementasi yang sangat mirip. KarenaArrayList
diimplementasikan menggunakan array dinamis, ukurannya bertambah sesuai permintaan. Jadi alasan konstruktor kapasitas adalah untuk keperluan optimasi.Ketika salah satu dari operasi resizings ini terjadi, ArrayList menyalin isi dari array ke dalam array baru yang dua kali kapasitas dari yang lama. Operasi ini berjalan dalam waktu O (n) .
Contoh
Berikut adalah contoh bagaimana
ArrayList
peningkatan ukuran:Jadi daftar dimulai dengan kapasitas
10
, ketika item ke-11 ditambahkan itu meningkat50% + 1
hingga16
. Pada item ke-17ArrayList
meningkat lagi ke25
dan seterusnya. Sekarang perhatikan contoh di mana kami membuat daftar di mana kapasitas yang diinginkan sudah dikenal sebagai1000000
. MembuatArrayList
konstruktor tanpa ukuran akan memanggilArrayList.add
1000000
waktu yang membutuhkan O (1) secara normal atau O (n) pada pengubahan ukuran.Bandingkan ini menggunakan konstruktor dan kemudian panggilan
ArrayList.add
yang dijamin berjalan di O (1) .Java vs C #
Java adalah seperti di atas, mulai
10
dan meningkatkan setiap ukuran di50% + 1
. C # mulai4
dan meningkat jauh lebih agresif, dua kali lipat pada setiap ukuran. Contoh1000000
menambahkan dari atas untuk C # menggunakan3097084
operasi.Referensi
sumber
Mengatur ukuran awal ArrayList, misalnya untuk
ArrayList<>(100)
, mengurangi berapa kali alokasi ulang memori internal harus terjadi.Contoh:
Seperti yang Anda lihat dalam contoh di atas - suatu
ArrayList
dapat diperluas jika perlu. Apa ini tidak menunjukkan kepada Anda adalah bahwa ukuran Arraylist biasanya berlipat ganda (walaupun perhatikan bahwa ukuran baru tergantung pada implementasi Anda). Berikut ini dikutip dari Oracle :Jelas, jika Anda tidak tahu kisaran apa yang akan Anda pegang, mengatur ukuran mungkin tidak akan menjadi ide yang baik - namun, jika Anda memiliki kisaran tertentu dalam pikiran, pengaturan kapasitas awal akan meningkatkan efisiensi memori .
sumber
ArrayList dapat berisi banyak nilai dan ketika melakukan penyisipan awal yang besar Anda dapat memberitahu ArrayList untuk mengalokasikan penyimpanan yang lebih besar untuk memulai dengan agar tidak membuang siklus CPU ketika mencoba mengalokasikan lebih banyak ruang untuk item berikutnya. Dengan demikian untuk mengalokasikan beberapa ruang di awal lebih efisien.
sumber
Ini untuk menghindari upaya yang mungkin untuk realokasi untuk setiap objek tunggal.
internal
new Object[]
dibuat.JVM perlu upaya untuk membuat
new Object[]
ketika Anda menambahkan elemen dalam daftar array. Jika Anda tidak memiliki kode diatas (setiap algo Anda berpikir) untuk realokasi maka setiap kali ketika Anda menjalankanarraylist.add()
kemudiannew Object[]
harus dibuat yang sia-sia dan kami kehilangan waktu untuk meningkatkan ukuran oleh 1 untuk setiap objek yang akan ditambahkan. Jadi lebih baik menambah ukuranObject[]
dengan formula berikut.(JSL telah menggunakan rumus forcasting yang diberikan di bawah ini untuk daftar array yang tumbuh secara dinamis alih-alih bertambah 1 setiap kali. Karena untuk tumbuh dibutuhkan upaya oleh JVM)
sumber
add
- sudah menggunakan beberapa formula pertumbuhan secara internal. Karena itu pertanyaannya tidak dijawab.int newCapacity = (oldCapacity * 3)/2 + 1;
yang hadir dalam kelas ArrayList. Apakah Anda masih berpikir itu belum terjawab?ArrayList
realokasi diamortisasi berlangsung di setiap kasus dengan setiap nilai untuk kapasitas awal. Dan pertanyaannya adalah tentang: Mengapa menggunakan nilai non-standar untuk kapasitas awal? Selain itu: "membaca yang tersirat" bukanlah sesuatu yang diinginkan dalam jawaban teknis. ;-)Saya pikir setiap ArrayList dibuat dengan nilai kapasitas init "10". Jadi, jika Anda membuat ArrayList tanpa menetapkan kapasitas dalam konstruktor, itu akan dibuat dengan nilai default.
sumber
Saya akan mengatakan ini sebuah optimasi. ArrayList tanpa kapasitas awal akan memiliki ~ 10 baris kosong dan akan diperluas ketika Anda melakukan add.
Untuk memiliki daftar dengan jumlah item yang Anda butuhkan untuk memanggil trimToSize ()
sumber
Sesuai pengalaman saya
ArrayList
, memberikan kapasitas awal adalah cara yang baik untuk menghindari biaya realokasi. Tapi itu menjadi peringatan. Semua saran yang disebutkan di atas mengatakan bahwa seseorang harus menyediakan kapasitas awal hanya ketika perkiraan kasar jumlah elemen diketahui. Tetapi ketika kami mencoba untuk memberikan kapasitas awal tanpa ide, jumlah memori yang dicadangkan dan tidak digunakan akan sia-sia karena mungkin tidak pernah diperlukan setelah daftar diisi ke sejumlah elemen yang diperlukan. Apa yang saya katakan adalah, kita bisa pragmatis di awal sambil mengalokasikan kapasitas, dan kemudian menemukan cara cerdas untuk mengetahui kapasitas minimal yang diperlukan saat runtime. ArrayList menyediakan metode yang disebutensureCapacity(int minCapacity)
. Tapi kemudian, seseorang telah menemukan cara yang cerdas ...sumber
Saya telah menguji ArrayList dengan dan tanpa initialCapacity dan saya mendapat hasil yang mengejutkan.
Ketika saya mengatur LOOP_NUMBER menjadi 100.000 atau kurang hasilnya adalah bahwa pengaturan initialCapacity lebih efisien.
Tetapi ketika saya mengatur LOOP_NUMBER menjadi 1.000.000 hasilnya berubah menjadi:
Akhirnya, saya tidak tahu bagaimana cara kerjanya ?!
Kode sampel:
Saya telah menguji pada windows8.1 dan jdk1.7.0_80
sumber