Prestasi mengandungi () dalam HashSet vs ArrayList

1. Pengenalan

Dalam panduan ringkas ini, kita akan membincangkan prestasi kaedah mengandung () yang terdapat di java.util. HashSet dan java.util. Senarai Array . Kedua-duanya adalah koleksi untuk menyimpan dan memanipulasi objek.

HashSet adalah koleksi untuk menyimpan elemen unik. Untuk mengetahui lebih lanjut mengenai HashSet, lihat pautan ini.

ArrayList adalah pelaksanaan popular antara muka java.util.List .

Kami mempunyai artikel panjang lebar mengenai ArrayList yang terdapat di sini.

2. HashSet.contains ()

Secara dalaman, pelaksanaan HashSet didasarkan pada instance HashMap . Kaedah mengandungi () memanggil HashMap.containsKey (objek) .

Di sini, ia memeriksa sama ada objek itu ada di peta dalaman atau tidak. Peta dalaman menyimpan data di dalam Node, yang dikenali sebagai baldi. Setiap baldi sesuai dengan kod hash yang dihasilkan dengan kaedah hashCode () . Jadi mengandungi () sebenarnya menggunakan kaedah hashCode () untuk mencari lokasi objek .

Sekarang mari kita tentukan kerumitan masa pencarian. Sebelum melangkah ke hadapan, pastikan anda sudah biasa dengan notasi Big-O.

Rata-rata, yang mengandungi () daripada HashSet berjalan di O (1) masa . Mendapatkan lokasi baldi objek adalah operasi masa yang berterusan. Dengan mengambil kira kemungkinan perlanggaran, waktu pencarian mungkin meningkat menjadi log (n) kerana struktur baldi dalaman adalah TreeMap .

Ini adalah peningkatan dari Java 7 yang menggunakan LinkedList untuk struktur baldi dalaman. Secara amnya, perlanggaran kod hash jarang berlaku. Oleh itu, kita boleh mempertimbangkan unsur-unsur lookup kerumitan sebagai O (1) .

3. ArrayList.c onains ()

Secara dalaman, ArrayList menggunakan kaedah indexOf (objek) untuk memeriksa sama ada objek itu ada dalam senarai . Kaedah indexOf (objek) mengulangi keseluruhan array dan membandingkan setiap elemen dengan kaedah sama (objek) .

Kembali ke analisis kerumitan, ArrayList . mengandungi () kaedah memerlukan masa O (n) . Jadi masa yang kita habiskan untuk mencari objek tertentu di sini bergantung pada jumlah item yang kita ada dalam array.

4. Ujian Penanda Aras

Sekarang, mari kita panaskan JVM dengan ujian penanda aras prestasi. Kami akan menggunakan produk OpenJDK JMH (Java Microbenchmark Harness). Untuk mengetahui lebih lanjut mengenai penyediaan dan pelaksanaan, lihat panduan berguna kami.

Untuk memulakan, mari buat kelas CollectionsBenchmark ringkas :

@BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Warmup(iterations = 5) public class CollectionsBenchmark { @State(Scope.Thread) public static class MyState { private Set employeeSet = new HashSet(); private List employeeList = new ArrayList(); private long iterations = 1000; private Employee employee = new Employee(100L, "Harry"); @Setup(Level.Trial) public void setUp() { for (long i = 0; i < iterations; i++) { employeeSet.add(new Employee(i, "John")); employeeList.add(new Employee(i, "John")); } employeeList.add(employee); employeeSet.add(employee); } } }

Di sini, kami membuat dan menginisialisasi HashSet dan objek ArrayList of Employee :

public class Employee { private Long id; private String name; // constructor and getter setters go here }

Kami menambahkan contoh pekerja = Pekerja baru (100L, "Harry") sebagai elemen terakhir untuk kedua koleksi. Oleh itu, kami menguji masa pencarian objek pekerja untuk kes terburuk.

@OutputTimeUnit (TimeUnit.NANOSECONDS) menunjukkan bahawa kami mahukan hasilnya dalam nanodetik. Jumlah lelaran @Warmup lalai adalah 5 dalam kes kami. The @BenchmarkMode ditetapkan kepada Mode.AverageTime , yang bermaksud kita berminat dalam mengira purata masa berjalan. Untuk pelaksanaan pertama, kami meletakkan iterasi = 1000 item dalam koleksi kami.

Selepas itu, kami menambahkan kaedah penanda aras kami ke kelas CollectionsBenchmark :

@Benchmark public boolean testArrayList(MyState state) { return state.employeeList.contains(state.employee); }

Di sini kita periksa sama ada senarai pekerja mengandungi objek pekerja .

Begitu juga, kami mempunyai ujian biasa untuk pekerja Set :

@Benchmark public boolean testHashSet(MyState state) { return state.employeeSet.contains(state.employee); }

Akhirnya, kita boleh menjalankan ujian:

public static void main(String[] args) throws Exception { Options options = new OptionsBuilder() .include(CollectionsBenchmark.class.getSimpleName()) .forks(1).build(); new Runner(options).run(); }

Inilah hasilnya:

Benchmark Mode Cnt Score Error Units CollectionsBenchmark.testArrayList avgt 20 4035.646 ± 598.541 ns/op CollectionsBenchmark.testHashSet avgt 20 9.456 ± 0.729 ns/op

Kita dapat melihat dengan jelas bahawa kaedah testArrayList mempunyai skor carian rata-rata 4035.646 ns , sementara testHashSet berkinerja lebih pantas dengan purata 9.456 ns .

Sekarang, mari tingkatkan jumlah elemen dalam ujian kami dan jalankan untuk iterasi = 10.000 item:

Benchmark Mode Cnt Score Error Units CollectionsBenchmark.testArrayList avgt 20 57499.620 ± 11388.645 ns/op CollectionsBenchmark.testHashSet avgt 20 11.802 ± 1.164 ns/op

Di sini juga, kandungan () dalam HashSet mempunyai kelebihan prestasi yang besar berbanding ArrayList .

5. Kesimpulan

Ini menulis-up cepat menjelaskan prestasi mengandungi () kaedah yang HashSet dan ArrayList koleksi. Dengan bantuan penanda aras JMH, kami telah menunjukkan prestasi berisi () untuk setiap jenis koleksi.

Sebagai kesimpulan, kita dapat belajar, bahawa kaedah mengandung () berfungsi lebih cepat di HashSet berbanding ArrayList .

Seperti biasa, kod lengkap untuk artikel ini sudah habis pada projek GitHub.