Performa berisi () dalam HashSet vs ArrayList

1. Perkenalan

Dalam panduan singkat ini, kita akan membahas kinerja metode contains () yang tersedia di java.util. HashSet dan java.util. ArrayList . Keduanya adalah koleksi untuk menyimpan dan memanipulasi objek.

HashSet adalah koleksi untuk menyimpan elemen unik. Untuk mempelajari lebih lanjut tentang HashSet, lihat tautan ini.

ArrayList adalah implementasi populer dari antarmuka java.util.List .

Kami memiliki artikel panjang tentang ArrayList yang tersedia di sini.

2. HashSet.contains ()

Secara internal, implementasi HashSet didasarkan pada instance HashMap . Metode contains () memanggil HashMap.containsKey (object) .

Di sini, memeriksa apakah objek ada di peta internal atau tidak. Peta internal menyimpan data di dalam Node, yang dikenal sebagai keranjang. Setiap keranjang sesuai dengan kode hash yang dibuat dengan metode hashCode () . Jadi contains () sebenarnya menggunakan metode hashCode () untuk menemukan lokasi objek .

Sekarang mari kita tentukan kompleksitas waktu pencarian. Sebelum melanjutkan, pastikan Anda terbiasa dengan notasi Big-O.

Rata-rata, para mengandung () dari HashSet berjalan di O (1) waktu . Mendapatkan objek ember lokasi adalah waktu operasi konstan. Dengan mempertimbangkan kemungkinan tabrakan, waktu pencarian dapat meningkat menjadi log (n) karena struktur keranjang internal adalah TreeMap .

Ini adalah peningkatan dari Java 7 yang menggunakan LinkedList untuk struktur keranjang internal. Secara umum, tabrakan kode hash jarang terjadi. Jadi kita dapat menganggap kompleksitas pencarian elemen sebagai O (1) .

3. ArrayList.c ontains ()

Secara internal, ArrayList menggunakan metode indexOf (object) untuk memeriksa apakah objek tersebut ada dalam daftar . Metode indexOf (object) mengulang seluruh array dan membandingkan setiap elemen dengan metode equals (object) .

Kembali ke analisis kompleksitas, ArrayList . Berisi () metode membutuhkan waktu O (n) . Jadi waktu yang kita habiskan untuk mencari objek tertentu di sini bergantung pada jumlah item yang kita miliki dalam array.

4. Pengujian Benchmark

Sekarang, mari kita pemanasan JVM dengan tes benchmark kinerja. Kami akan menggunakan produk OpenJDK JMH (Java Microbenchmark Harness). Untuk mempelajari lebih lanjut tentang penyiapan dan eksekusi, lihat panduan bermanfaat kami.

Untuk memulai, mari buat kelas CollectionsBenchmark sederhana :

@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, kita membuat dan menginisialisasi HashSet dan ArrayList dari Employee objek:

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

Kami menambahkan instance employee = new Employee (100L, "Harry") sebagai elemen terakhir untuk kedua koleksi. Jadi kami menguji waktu pencarian objek karyawan untuk kemungkinan kasus terburuk.

@OutputTimeUnit (TimeUnit.NANOSECONDS) menunjukkan bahwa kita menginginkan hasil dalam nanodetik. Jumlah iterasi @Warmup default adalah 5 dalam kasus kami. The @BenchmarkMode diatur untuk Mode.AverageTime , yang berarti kita tertarik dalam menghitung waktu berjalan rata-rata. Untuk eksekusi pertama, kami menempatkan iterasi = 1000 item di koleksi kami.

Setelah itu, kami menambahkan metode benchmark kami ke kelas CollectionsBenchmark :

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

Di sini kami memeriksa apakah employeeList berisi objek karyawan .

Demikian juga, kami memiliki tes familiar untuk employeeSet :

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

Akhirnya, kami dapat menjalankan pengujian:

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

Berikut 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 bahwa metode testArrayList memiliki skor pencarian rata-rata 4035.646 ns , sedangkan testHashSet bekerja lebih cepat dengan rata-rata 9.456 ns .

Sekarang, mari kita tingkatkan jumlah elemen dalam pengujian kita dan menjalankannya 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, contains () di HashSet memiliki keunggulan kinerja yang sangat besar dibandingkan ArrayList .

5. Kesimpulan

Tulisan cepat ini menjelaskan kinerja metode contains () dari koleksi HashSet dan ArrayList . Dengan bantuan pembandingan JMH, kami telah menyajikan kinerja berisi () untuk setiap jenis koleksi.

Sebagai kesimpulan, kita dapat belajar, bahwa metode contains () bekerja lebih cepat di HashSet dibandingkan dengan ArrayList .

Seperti biasa, kode lengkap untuk artikel ini selesai pada proyek GitHub.