Mengapa mulai ArrayList dengan kapasitas awal?

Biasa konstruktor ArrayList adalah:

ArrayList<?> list = new ArrayList<>();

Tapi ada juga yang overload constructor dengan satu parameter untuk kapasitas awal:

ArrayList<?> list = new ArrayList<>(20);

Mengapa ini berguna untuk membuat sebuah ArrayList dengan kapasitas awal ketika kita dapat menambahkan untuk itu seperti yang kita tolong?

Mengomentari pertanyaan (4)
Larutan

Jika anda tahu terlebih dahulu apa ukuran ArrayList ini akan terjadi, hal ini lebih efisien untuk menentukan kapasitas awal. Jika anda don't melakukan hal ini, internal array harus berulang kali dialokasikan sebagai daftar tumbuh.

Yang lebih besar daftar akhir, semakin banyak waktu yang anda simpan dengan menghindari realokasi.

Yang mengatakan, bahkan tanpa pra-alokasi, memasukkan n elemen di bagian belakang sebuah ArrayList dijamin untuk mengambil total O(n) waktu. Dengan kata lain, menambahkan sebuah elemen yang diamortisasi konstan-waktu operasi. Hal ini dicapai oleh masing-masing memiliki realokasi meningkatkan ukuran dari array secara eksponensial, biasanya dengan faktor1.5. Dengan pendekatan ini, total jumlah operasi [dapat ditunjukkan untuk menjadiO(n)`]1.

Komentar (11)

Karena ArrayList adalah secara dinamis mengubah ukuran array struktur data, yang berarti hal ini diimplementasikan sebagai array dengan awal (default) ukuran tetap. Ketika hal ini akan diisi, array akan diperpanjang untuk ganda berukuran satu. Operasi ini lebih mahal, sehingga anda ingin sesedikit mungkin.

Jadi, jika anda tahu anda terikat atas adalah 20 item, kemudian membuat array dengan panjang awal 20 lebih baik daripada menggunakan default dari, katakanlah, 15 dan kemudian mengubah ukuran ke 15*2 = 30 dan gunakan hanya 20 sementara buang siklus untuk ekspansi.

P. S. - Sebagai AmitG mengatakan, perluasan faktor implementasi tertentu (dalam hal ini (oldCapacity * 3)/2 + 1)

Komentar (2)

Default ukuran Arraylist adalah 10.

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
    this(10);
    } 

Jadi jika anda akan menambahkan 100 atau lebih catatan, anda dapat melihat overhead memori realokasi.

ArrayList<?> list = new ArrayList();    
// same as  new ArrayList(10);      

Jadi jika anda memiliki gagasan tentang jumlah elemen yang akan disimpan dalam Arraylist yang lebih baik untuk membuat Arraylist dengan ukuran yang bukannya mulai dengan 10 dan kemudian akan meningkat.

Komentar (1)

Saya benar-benar menulis blog post pada topik 2 bulan yang lalu. Artikel ini adalah untuk C#'s Daftar<T> tapi Java's ArrayList sangat mirip implementasi. Sejak ArrayList ini diimplementasikan menggunakan dynamic array, meningkatkan ukuran sesuai permintaan. Jadi alasan kapasitas constructor adalah untuk optimalisasi tujuan.

Ketika salah satu dari ini resizings operasi terjadi, ArrayList salinan isi dari array ke array baru yang dua kali kapasitas dari yang lama. Operasi ini berjalan di O(n) waktu.

Contoh

Berikut adalah contoh bagaimana ArrayList akan meningkat dalam ukuran:

10
16
25
38
58
... 17 resizes ...
198578
297868
446803
670205
1005308

Jadi daftar dimulai dengan kapasitas 10, ketika 11 item ditambahkan itu meningkat 50% + 1 ke 16. Pada tanggal 17 item ArrayList lebih meningkat lagi untuk 25 dan sebagainya. Sekarang perhatikan contoh di mana kita're menciptakan sebuah daftar di mana kapasitas yang diinginkan sudah dikenal sebagai 1000000. Menciptakan ArrayList tanpa ukuran konstruktor akan memanggil ArrayList.tambahkan 1000000 kali yang memakan waktu O(1) normal atau O(n) di resize.

1000000 + 16 + 25 + ... + 670205 + 1005308 = 4015851 operasi

Bandingkan ini dengan menggunakan constructor dan kemudian memanggil ArrayList.tambahkan yang dijamin untuk menjalankan di O(1).

1000000 + 1000000 = 2000000 operasi

Jawa vs C#

Jawa adalah seperti di atas, mulai pukul 10 dan meningkatkan masing-masing ukuran di 50% + 1. C# dimulai pada 4 dan meningkat jauh lebih agresif, dua kali lipat pada masing-masing ukuran. The 1000000 menambahkan contoh di atas untuk C# menggunakan 3097084 operasi.

Referensi

Komentar (0)

Pengaturan awal ukuran ArrayList, misalnya untuk ArrayList<>(100), mengurangi jumlah kali re-alokasi memori internal yang telah terjadi.

Contoh:

ArrayList example = new ArrayList(3);
example.add(1); // size() == 1
example.add(2); // size() == 2, 
example.add(2); // size() == 3, example has been 'filled'
example.add(3); // size() == 4, example has been 'expanded' so that the fourth element can be added. 

Seperti yang anda lihat dalam contoh di atas - sebuah ArrayList dapat diperluas jika perlu. Apa ini doesn't menunjukkan bahwa ukuran Arraylist biasanya ganda (meskipun catatan yang baru ukuran tergantung pada implementasi). Berikut ini dikutip dari Oracle:

"masing-Masing ArrayList contoh memiliki kapasitas. Kapasitas adalah ukuran array yang digunakan untuk menyimpan elemen-elemen dalam list. Itu selalu di setidaknya sebagai besar seperti daftar ukuran. Sebagai elemen yang ditambahkan ke ArrayList, kapasitas tumbuh secara otomatis. Rincian pertumbuhan kebijakan yang tidak ditentukan di luar fakta bahwa menambahkan elemen memiliki konstan diamortisasi biaya waktu."

Jelas, jika anda tidak memiliki ide seperti apa jenis dari jangkauan anda akan memegang, pengaturan size mungkin tidak't menjadi ide yang baik - namun, jika anda memiliki kisaran tertentu dalam pikiran, pengaturan kapasitas awal akan meningkatkan efisiensi memori.

Komentar (0)

ArrayList dapat mengandung banyak nilai-nilai dan ketika melakukan awal yang besar insersi anda dapat memberitahu ArrayList untuk mengalokasikan penyimpanan yang lebih besar untuk mulai dengan tidak membuang-buang siklus CPU ketika mencoba untuk mengalokasikan lebih banyak ruang untuk item berikutnya. Dengan demikian untuk mengalokasikan beberapa ruang di awal lebih effiecient.

Komentar (0)

Hal ini untuk menghindari kemungkinan upaya untuk realokasi untuk setiap objek tunggal.

int newCapacity = (oldCapacity * 3)/2 + 1;

secara internal new Object[] dibuat.
JVM perlu upaya untuk membuat new Object[] ketika anda menambahkan elemen dalam arraylist. Jika anda don't memiliki kode di atas(setiap algo yang anda pikirkan) untuk realokasi maka setiap kali ketika anda memanggil arraylist.add() kemudian new Object[] harus dibuat yang sia-sia dan kita kehilangan waktu untuk meningkatkan ukuran dengan 1 untuk setiap objek yang akan ditambahkan. Jadi lebih baik untuk meningkatkan ukuran Object[] dengan rumus berikut.
(JSL telah digunakan forcasting rumus yang diberikan di bawah ini untuk tumbuh secara dinamis arraylist bukannya tumbuh sebesar 1 setiap waktu. Karena untuk menumbuhkan dibutuhkan upaya oleh JVM)

int newCapacity = (oldCapacity * 3)/2 + 1;
Komentar (4)

I'd mengatakan optimasi. ArrayList tanpa kapasitas awal akan memiliki ~10 baris kosong dan akan memperluas ketika anda melakukan add.

Untuk memiliki daftar dengan persis jumlah item yang anda butuhkan untuk memanggil trimToSize()

Komentar (0)

Saya pikir masing-masing ArrayList dibuat dengan init nilai kapasitas dari "10". Jadi, jika anda membuat ArrayList tanpa pengaturan kapasitas di dalam constructor ini akan dibuat dengan nilai default.

Komentar (0)

Sesuai pengalaman saya dengan ArrayList, memberikan kapasitas awal adalah cara yang bagus untuk menghindari realokasi biaya. Tapi ini beruang peringatan. Semua saran yang disebutkan di atas mengatakan bahwa salah satu harus menyediakan kapasitas awal hanya ketika perkiraan kasar dari jumlah unsur-unsur yang diketahui. Tetapi ketika kita mencoba untuk memberikan kapasitas awal tanpa ide, jumlah memori yang dilindungi dan yang tidak terpakai akan menjadi sia-sia seperti itu mungkin tidak akan pernah diperlukan sekali daftar ini diisi dengan jumlah yang diperlukan dari unsur-unsur. Apa yang saya katakan adalah, kita dapat pragmatis pada awal sementara mengalokasikan kapasitas, dan kemudian menemukan cara cerdas untuk mengetahui diperlukan minimal kapasitas pada saat runtime. ArrayList menyediakan sebuah metode yang disebut ensureCapacity(int murah untuk kontainer ukuran). Tapi kemudian, satu telah menemukan cara cerdas...

Komentar (0)

Saya telah diuji ArrayList dengan dan tanpa initialCapacity dan saya mendapat hasil mengejutkan
Ketika saya set LOOP_NUMBER 100.000 atau kurang hasilnya adalah bahwa pengaturan initialCapacity lebih efisien.

list1Sttop-list1Start = 14
list2Sttop-list2Start = 10


Tapi ketika saya set LOOP_NUMBER 1.000.000 hasil perubahan:

list1Stop-list1Start = 40
list2Stop-list2Start = 66


Akhirnya, saya tidak't tahu bagaimana cara kerjanya?!
Contoh kode:

 public static final int LOOP_NUMBER = 100000;

public static void main(String[] args) {

    long list1Start = System.currentTimeMillis();
    List list1 = new ArrayList();
    for (int i = 0; i < LOOP_NUMBER; i++) {
        list1.add(i);
    }
    long list1Stop = System.currentTimeMillis();
    System.out.println("list1Stop-list1Start = " + String.valueOf(list1Stop - list1Start));

    long list2Start = System.currentTimeMillis();
    List list2 = new ArrayList(LOOP_NUMBER);
    for (int i = 0; i < LOOP_NUMBER; i++) {
        list2.add(i);
    }
    long list2Stop = System.currentTimeMillis();
    System.out.println("list2Stop-list2Start = " + String.valueOf(list2Stop - list2Start));
}

Saya telah diuji pada windows8.1 dan jdk1.7.0_80

Komentar (1)